]> git.sesse.net Git - vlc/blob - modules/demux/mkv/chapters.cpp
eb359baf78b4f67a1ce476edfaf09785c598396a
[vlc] / modules / demux / mkv / chapters.cpp
1 /*****************************************************************************
2  * mkv.cpp : matroska demuxer
3  *****************************************************************************
4  * Copyright (C) 2003-2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Steve Lhomme <steve.lhomme@free.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "chapters.hpp"
26
27 #include "chapter_command.hpp"
28
29 chapter_item_c::~chapter_item_c()
30 {
31     std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
32     while ( index != codecs.end() )
33     {
34         delete (*index);
35         index++;
36     }
37     std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
38     while ( index_ != sub_chapters.end() )
39     {
40         delete (*index_);
41         index_++;
42     }
43 }
44
45 int chapter_item_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
46 {
47     // add support for meta-elements from codec like DVD Titles
48     if ( !b_display_seekpoint || psz_name == "" )
49     {
50         psz_name = GetCodecName();
51         if ( psz_name != "" )
52             b_display_seekpoint = true;
53     }
54
55     if (b_display_seekpoint)
56     {
57         seekpoint_t *sk = vlc_seekpoint_New();
58
59         sk->i_level = i_level;
60         sk->i_time_offset = i_start_time;
61         sk->psz_name = strdup( psz_name.c_str() );
62
63         // A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value.
64         title.i_seekpoint++;
65         title.seekpoint = (seekpoint_t**)realloc( title.seekpoint, title.i_seekpoint * sizeof( seekpoint_t* ) );
66         title.seekpoint[title.i_seekpoint-1] = sk;
67
68         if ( b_user_display )
69             i_user_chapters++;
70     }
71
72     for ( size_t i=0; i<sub_chapters.size() ; i++)
73     {
74         sub_chapters[i]->PublishChapters( title, i_user_chapters, i_level+1 );
75     }
76
77     i_seekpoint_num = i_user_chapters;
78
79     return i_user_chapters;
80 }
81
82 chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id,
83                                     bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
84                                     const void *p_cookie,
85                                     size_t i_cookie_size )
86 {
87     // this chapter
88     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
89     while ( index != codecs.end() )
90     {
91         if ( match( **index ,p_cookie, i_cookie_size ) )
92             return this;
93         index++;
94     }
95  
96     // sub-chapters
97     chapter_item_c *p_result = NULL;
98     std::vector<chapter_item_c*>::const_iterator index2 = sub_chapters.begin();
99     while ( index2 != sub_chapters.end() )
100     {
101         p_result = (*index2)->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
102         if ( p_result != NULL )
103             return p_result;
104         index2++;
105     }
106  
107     return p_result;
108 }
109
110 void chapter_item_c::Append( const chapter_item_c & chapter )
111 {
112     // we are appending content for the same chapter UID
113     size_t i;
114     chapter_item_c *p_chapter;
115
116     for ( i=0; i<chapter.sub_chapters.size(); i++ )
117     {
118         p_chapter = FindChapter( chapter.sub_chapters[i]->i_uid );
119         if ( p_chapter != NULL )
120         {
121             p_chapter->Append( *chapter.sub_chapters[i] );
122         }
123         else
124         {
125             sub_chapters.push_back( chapter.sub_chapters[i] );
126         }
127     }
128
129     i_user_start_time = min( i_user_start_time, chapter.i_user_start_time );
130     i_user_end_time = max( i_user_end_time, chapter.i_user_end_time );
131 }
132
133 chapter_item_c * chapter_item_c::FindChapter( int64_t i_find_uid )
134 {
135     size_t i;
136     chapter_item_c *p_result = NULL;
137
138     if ( i_uid == i_find_uid )
139         return this;
140
141     for ( i=0; i<sub_chapters.size(); i++)
142     {
143         p_result = sub_chapters[i]->FindChapter( i_find_uid );
144         if ( p_result != NULL )
145             break;
146     }
147     return p_result;
148 }
149
150 std::string chapter_item_c::GetCodecName( bool f_for_title ) const
151 {
152     std::string result;
153
154     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
155     while ( index != codecs.end() )
156     {
157         result = (*index)->GetCodecName( f_for_title );
158         if ( result != "" )
159             break;
160         index++;
161     }
162
163     return result;
164 }
165
166 int16 chapter_item_c::GetTitleNumber( ) const
167 {
168     int result = -1;
169
170     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
171     while ( index != codecs.end() )
172     {
173         result = (*index)->GetTitleNumber( );
174         if ( result >= 0 )
175             break;
176         index++;
177     }
178
179     return result;
180 }
181
182 int64_t chapter_item_c::RefreshChapters( bool b_ordered, int64_t i_prev_user_time )
183 {
184     int64_t i_user_time = i_prev_user_time;
185  
186     // first the sub-chapters, and then ourself
187     std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
188     while ( index != sub_chapters.end() )
189     {
190         i_user_time = (*index)->RefreshChapters( b_ordered, i_user_time );
191         index++;
192     }
193
194     if ( b_ordered )
195     {
196         // the ordered chapters always start at zero
197         if ( i_prev_user_time == -1 )
198         {
199             if ( i_user_time == -1 )
200                 i_user_time = 0;
201             i_prev_user_time = 0;
202         }
203
204         i_user_start_time = i_prev_user_time;
205         if ( i_end_time != -1 && i_user_time == i_prev_user_time )
206         {
207             i_user_end_time = i_user_start_time - i_start_time + i_end_time;
208         }
209         else
210         {
211             i_user_end_time = i_user_time;
212         }
213     }
214     else
215     {
216         if ( sub_chapters.begin() != sub_chapters.end() )
217             std::sort( sub_chapters.begin(), sub_chapters.end(), chapter_item_c::CompareTimecode );
218         i_user_start_time = i_start_time;
219         if ( i_end_time != -1 )
220             i_user_end_time = i_end_time;
221         else if ( i_user_time != -1 )
222             i_user_end_time = i_user_time;
223         else
224             i_user_end_time = i_user_start_time;
225     }
226
227     return i_user_end_time;
228 }
229
230 chapter_item_c *chapter_item_c::FindTimecode( mtime_t i_user_timecode, const chapter_item_c * p_current, bool & b_found )
231 {
232     chapter_item_c *psz_result = NULL;
233
234     if ( p_current == this )
235         b_found = true;
236
237     if ( i_user_timecode >= i_user_start_time &&
238         ( i_user_timecode < i_user_end_time ||
239           ( i_user_start_time == i_user_end_time && i_user_timecode == i_user_end_time )))
240     {
241         std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
242         while ( index != sub_chapters.end() && ((p_current == NULL && psz_result == NULL) || (p_current != NULL && (!b_found || psz_result == NULL))))
243         {
244             psz_result = (*index)->FindTimecode( i_user_timecode, p_current, b_found );
245             index++;
246         }
247  
248         if ( psz_result == NULL )
249             psz_result = this;
250     }
251
252     return psz_result;
253 }
254
255 bool chapter_item_c::ParentOf( const chapter_item_c & item ) const
256 {
257     if ( &item == this )
258         return true;
259
260     std::vector<chapter_item_c*>::const_iterator index = sub_chapters.begin();
261     while ( index != sub_chapters.end() )
262     {
263         if ( (*index)->ParentOf( item ) )
264             return true;
265         index++;
266     }
267
268     return false;
269 }
270
271 bool chapter_item_c::Enter( bool b_do_subs )
272 {
273     bool f_result = false;
274     std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
275     while ( index != codecs.end() )
276     {
277         f_result |= (*index)->Enter();
278         index++;
279     }
280
281     if ( b_do_subs )
282     {
283         // sub chapters
284         std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
285         while ( index_ != sub_chapters.end() )
286         {
287             f_result |= (*index_)->Enter( true );
288             index_++;
289         }
290     }
291     return f_result;
292 }
293
294 bool chapter_item_c::Leave( bool b_do_subs )
295 {
296     bool f_result = false;
297     b_is_leaving = true;
298     std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
299     while ( index != codecs.end() )
300     {
301         f_result |= (*index)->Leave();
302         index++;
303     }
304
305     if ( b_do_subs )
306     {
307         // sub chapters
308         std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
309         while ( index_ != sub_chapters.end() )
310         {
311             f_result |= (*index_)->Leave( true );
312             index_++;
313         }
314     }
315     b_is_leaving = false;
316     return f_result;
317 }
318
319 bool chapter_item_c::EnterAndLeave( chapter_item_c *p_item, bool b_final_enter )
320 {
321     chapter_item_c *p_common_parent = p_item;
322
323     // leave, up to a common parent
324     while ( p_common_parent != NULL && !p_common_parent->ParentOf( *this ) )
325     {
326         if ( !p_common_parent->b_is_leaving && p_common_parent->Leave( false ) )
327             return true;
328         p_common_parent = p_common_parent->psz_parent;
329     }
330
331     // enter from the parent to <this>
332     if ( p_common_parent != NULL )
333     {
334         do
335         {
336             if ( p_common_parent == this )
337                 return Enter( true );
338
339             for ( size_t i = 0; i<p_common_parent->sub_chapters.size(); i++ )
340             {
341                 if ( p_common_parent->sub_chapters[i]->ParentOf( *this ) )
342                 {
343                     p_common_parent = p_common_parent->sub_chapters[i];
344                     if ( p_common_parent != this )
345                         if ( p_common_parent->Enter( false ) )
346                             return true;
347
348                     break;
349                 }
350             }
351         } while ( 1 );
352     }
353
354     if ( b_final_enter )
355         return Enter( true );
356     else
357         return false;
358 }
359
360
361
362 /* Chapter Edition Class */
363 std::string chapter_edition_c::GetMainName() const
364 {
365     if ( sub_chapters.size() )
366     {
367         return sub_chapters[0]->GetCodecName( true );
368     }
369     return "";
370 }
371
372 void chapter_edition_c::RefreshChapters( )
373 {
374     chapter_item_c::RefreshChapters( b_ordered, -1 );
375     b_display_seekpoint = false;
376 }
377
378 mtime_t chapter_edition_c::Duration() const
379 {
380     mtime_t i_result = 0;
381  
382     if ( sub_chapters.size() )
383     {
384         std::vector<chapter_item_c*>::const_iterator index = sub_chapters.end();
385         index--;
386         i_result = (*index)->i_user_end_time;
387     }
388  
389     return i_result;
390 }
391
392 chapter_item_c * chapter_edition_c::FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current )
393 {
394     if ( !b_ordered )
395         p_current = NULL;
396     bool b_found_current = false;
397     return chapter_item_c::FindTimecode( i_timecode, p_current, b_found_current );
398 }
399
400