]> git.sesse.net Git - vlc/blob - modules/demux/mkv/chapters.cpp
contrib: ffmpeg: don't bother with --enable-neon and --enable-iwmmxt
[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     vlc_delete_all( codecs );
32     vlc_delete_all( sub_chapters );
33 }
34
35 int chapter_item_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
36 {
37     // add support for meta-elements from codec like DVD Titles
38     if ( !b_display_seekpoint || psz_name == "" )
39     {
40         psz_name = GetCodecName();
41         if ( psz_name != "" )
42             b_display_seekpoint = true;
43     }
44
45     if (b_display_seekpoint)
46     {
47         seekpoint_t *sk = vlc_seekpoint_New();
48
49         sk->i_level = i_level;
50         sk->i_time_offset = i_start_time;
51         sk->psz_name = strdup( psz_name.c_str() );
52
53         // A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value.
54         title.i_seekpoint++;
55         title.seekpoint = (seekpoint_t**)xrealloc( title.seekpoint,
56                                  title.i_seekpoint * sizeof( seekpoint_t* ) );
57         title.seekpoint[title.i_seekpoint-1] = sk;
58
59         if ( b_user_display )
60             i_user_chapters++;
61     }
62
63     for ( size_t i=0; i<sub_chapters.size() ; i++)
64     {
65         sub_chapters[i]->PublishChapters( title, i_user_chapters, i_level+1 );
66     }
67
68     i_seekpoint_num = i_user_chapters;
69
70     return i_user_chapters;
71 }
72
73 chapter_item_c *chapter_item_c::BrowseCodecPrivate( unsigned int codec_id,
74                                     bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
75                                     const void *p_cookie,
76                                     size_t i_cookie_size )
77 {
78     // this chapter
79     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
80     while ( index != codecs.end() )
81     {
82         if ( match( **index ,p_cookie, i_cookie_size ) )
83             return this;
84         ++index;
85     }
86
87     // sub-chapters
88     chapter_item_c *p_result = NULL;
89     std::vector<chapter_item_c*>::const_iterator index2 = sub_chapters.begin();
90     while ( index2 != sub_chapters.end() )
91     {
92         p_result = (*index2)->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
93         if ( p_result != NULL )
94             return p_result;
95         ++index2;
96     }
97
98     return p_result;
99 }
100
101 void chapter_item_c::Append( const chapter_item_c & chapter )
102 {
103     // we are appending content for the same chapter UID
104     size_t i;
105     chapter_item_c *p_chapter;
106
107     for ( i=0; i<chapter.sub_chapters.size(); i++ )
108     {
109         p_chapter = FindChapter( chapter.sub_chapters[i]->i_uid );
110         if ( p_chapter != NULL )
111         {
112             p_chapter->Append( *chapter.sub_chapters[i] );
113         }
114         else
115         {
116             sub_chapters.push_back( chapter.sub_chapters[i] );
117         }
118     }
119
120     i_user_start_time = min( i_user_start_time, chapter.i_user_start_time );
121     i_user_end_time = max( i_user_end_time, chapter.i_user_end_time );
122 }
123
124 chapter_item_c * chapter_item_c::FindChapter( int64_t i_find_uid )
125 {
126     size_t i;
127     chapter_item_c *p_result = NULL;
128
129     if ( i_uid == i_find_uid )
130         return this;
131
132     for ( i=0; i<sub_chapters.size(); i++)
133     {
134         p_result = sub_chapters[i]->FindChapter( i_find_uid );
135         if ( p_result != NULL )
136             break;
137     }
138     return p_result;
139 }
140
141 std::string chapter_item_c::GetCodecName( bool f_for_title ) const
142 {
143     std::string result;
144
145     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
146     while ( index != codecs.end() )
147     {
148         result = (*index)->GetCodecName( f_for_title );
149         if ( result != "" )
150             break;
151         ++index;
152     }
153
154     return result;
155 }
156
157 int16 chapter_item_c::GetTitleNumber( ) const
158 {
159     int result = -1;
160
161     std::vector<chapter_codec_cmds_c*>::const_iterator index = codecs.begin();
162     while ( index != codecs.end() )
163     {
164         result = (*index)->GetTitleNumber( );
165         if ( result >= 0 )
166             break;
167         ++index;
168     }
169
170     return result;
171 }
172
173 int64_t chapter_item_c::RefreshChapters( bool b_ordered, int64_t i_prev_user_time )
174 {
175     int64_t i_user_time = i_prev_user_time;
176
177     // first the sub-chapters, and then ourself
178     std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
179     while ( index != sub_chapters.end() )
180     {
181         i_user_time = (*index)->RefreshChapters( b_ordered, i_user_time );
182         ++index;
183     }
184
185     if ( b_ordered )
186     {
187         // the ordered chapters always start at zero
188         if ( i_prev_user_time == -1 )
189         {
190             if ( i_user_time == -1 )
191                 i_user_time = 0;
192             i_prev_user_time = 0;
193         }
194
195         i_user_start_time = i_prev_user_time;
196         if ( i_end_time != -1 && i_user_time == i_prev_user_time )
197         {
198             i_user_end_time = i_user_start_time - i_start_time + i_end_time;
199         }
200         else
201         {
202             i_user_end_time = i_user_time;
203         }
204     }
205     else
206     {
207         if ( sub_chapters.begin() != sub_chapters.end() )
208             std::sort( sub_chapters.begin(), sub_chapters.end(), chapter_item_c::CompareTimecode );
209         i_user_start_time = i_start_time;
210         if ( i_end_time != -1 )
211             i_user_end_time = i_end_time;
212         else if ( i_user_time != -1 )
213             i_user_end_time = i_user_time;
214         else
215             i_user_end_time = i_user_start_time;
216     }
217
218     return i_user_end_time;
219 }
220
221 chapter_item_c *chapter_item_c::FindTimecode( mtime_t i_user_timecode, const chapter_item_c * p_current, bool & b_found )
222 {
223     chapter_item_c *psz_result = NULL;
224
225     if ( p_current == this )
226         b_found = true;
227
228     if ( i_user_timecode >= i_user_start_time &&
229         ( i_user_timecode < i_user_end_time ||
230           ( i_user_start_time == i_user_end_time && i_user_timecode == i_user_end_time )))
231     {
232         std::vector<chapter_item_c*>::iterator index = sub_chapters.begin();
233         while ( index != sub_chapters.end() && ((p_current == NULL && psz_result == NULL) || (p_current != NULL && (!b_found || psz_result == NULL))))
234         {
235             psz_result = (*index)->FindTimecode( i_user_timecode, p_current, b_found );
236             ++index;
237         }
238
239         if ( psz_result == NULL )
240             psz_result = this;
241     }
242
243     return psz_result;
244 }
245
246 bool chapter_item_c::ParentOf( const chapter_item_c & item ) const
247 {
248     if ( &item == this )
249         return true;
250
251     std::vector<chapter_item_c*>::const_iterator index = sub_chapters.begin();
252     while ( index != sub_chapters.end() )
253     {
254         if ( (*index)->ParentOf( item ) )
255             return true;
256         ++index;
257     }
258
259     return false;
260 }
261
262 bool chapter_item_c::Enter( bool b_do_subs )
263 {
264     bool f_result = false;
265     std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
266     while ( index != codecs.end() )
267     {
268         f_result |= (*index)->Enter();
269         ++index;
270     }
271
272     if ( b_do_subs )
273     {
274         // sub chapters
275         std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
276         while ( index_ != sub_chapters.end() )
277         {
278             f_result |= (*index_)->Enter( true );
279             ++index_;
280         }
281     }
282     return f_result;
283 }
284
285 bool chapter_item_c::Leave( bool b_do_subs )
286 {
287     bool f_result = false;
288     b_is_leaving = true;
289     std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
290     while ( index != codecs.end() )
291     {
292         f_result |= (*index)->Leave();
293         ++index;
294     }
295
296     if ( b_do_subs )
297     {
298         // sub chapters
299         std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
300         while ( index_ != sub_chapters.end() )
301         {
302             f_result |= (*index_)->Leave( true );
303             ++index_;
304         }
305     }
306     b_is_leaving = false;
307     return f_result;
308 }
309
310 bool chapter_item_c::EnterAndLeave( chapter_item_c *p_item, bool b_final_enter )
311 {
312     chapter_item_c *p_common_parent = p_item;
313
314     // leave, up to a common parent
315     while ( p_common_parent != NULL && !p_common_parent->ParentOf( *this ) )
316     {
317         if ( !p_common_parent->b_is_leaving && p_common_parent->Leave( false ) )
318             return true;
319         p_common_parent = p_common_parent->p_parent;
320     }
321
322     // enter from the parent to <this>
323     if ( p_common_parent != NULL )
324     {
325         do
326         {
327             if ( p_common_parent == this )
328                 return Enter( true );
329
330             for ( size_t i = 0; i<p_common_parent->sub_chapters.size(); i++ )
331             {
332                 if ( p_common_parent->sub_chapters[i]->ParentOf( *this ) )
333                 {
334                     p_common_parent = p_common_parent->sub_chapters[i];
335                     if ( p_common_parent != this )
336                         if ( p_common_parent->Enter( false ) )
337                             return true;
338
339                     break;
340                 }
341             }
342         } while ( 1 );
343     }
344
345     if ( b_final_enter )
346         return Enter( true );
347     else
348         return false;
349 }
350
351
352
353 /* Chapter Edition Class */
354 std::string chapter_edition_c::GetMainName() const
355 {
356     if ( sub_chapters.size() )
357     {
358         return sub_chapters[0]->GetCodecName( true );
359     }
360     return "";
361 }
362
363 void chapter_edition_c::RefreshChapters( )
364 {
365     chapter_item_c::RefreshChapters( b_ordered, -1 );
366     b_display_seekpoint = false;
367 }
368
369 mtime_t chapter_edition_c::Duration() const
370 {
371     mtime_t i_result = 0;
372
373     if ( sub_chapters.size() )
374     {
375         std::vector<chapter_item_c*>::const_iterator index = sub_chapters.end();
376         --index;
377         i_result = (*index)->i_user_end_time;
378     }
379
380     return i_result;
381 }
382
383 chapter_item_c * chapter_edition_c::FindTimecode( mtime_t i_timecode, const chapter_item_c * p_current )
384 {
385     if ( !b_ordered )
386         p_current = NULL;
387     bool b_found_current = false;
388     return chapter_item_c::FindTimecode( i_timecode, p_current, b_found_current );
389 }
390
391