source: trunk/MatroskaImport.cpp @ 334

Last change on this file since 334 was 334, checked in by astrange, 9 years ago

Hack overlap handling to make sure subtitle samples really don't overlap, which makes QT act really weird.
Restore some bit of control flow in the Matroska importer (which I don't quite understand).
Scale \fs tag properly.
Fix possibility of colorspace converter crashing (ffmpeg only guarantees 8-byte alignment and sse2 needs 16).

  • Property svn:executable set to *
File size: 9.3 KB
Line 
1/*
2 *  MatroskaImport.cpp
3 *
4 *    MatroskaImport.cpp - QuickTime importer interface for opening a Matroska file.
5 *
6 *
7 *  Copyright (c) 2006  David Conrad
8 *
9 *  This program is free software; you can redistribute it and/or
10 *  modify it under the terms of the GNU Lesser General Public
11 *  License as published by the Free Software Foundation;
12 *  version 2.1 of the License.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 *  Lesser General Public License for more details.
18 *
19 *  You should have received a copy of the GNU Lesser General Public
20 *  License along with this program; if not, write to the Free Software
21 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 */
24
25#include <Carbon/Carbon.h>
26#include <QuickTime/QuickTime.h>
27
28#include <matroska/KaxSegment.h>
29#include <matroska/KaxCluster.h>
30#include <matroska/KaxBlock.h>
31
32#include "MatroskaImportVersion.h"
33#include "MatroskaImport.h"
34#include "SubImport.h"
35#include "Codecprintf.h"
36
37extern "C" 
38ComponentResult create_placeholder_track(Movie movie, Track *placeholderTrack, TimeValue duration, Handle dataRef, OSType dataRefType);
39
40using namespace libmatroska;
41
42#pragma mark- Component Dispatch
43
44// Setup required for ComponentDispatchHelper.c since I don't feel like writing a dispatcher
45// for a C++ class akin to Apple's ACCodec.cpp classes for Core Audio codecs.
46#define MOVIEIMPORT_BASENAME()          MatroskaImport
47#define MOVIEIMPORT_GLOBALS()           MatroskaImport *storage
48
49#define CALLCOMPONENT_BASENAME()        MOVIEIMPORT_BASENAME()
50#define CALLCOMPONENT_GLOBALS()         MOVIEIMPORT_GLOBALS()
51
52#define COMPONENT_DISPATCH_FILE         "MatroskaImportDispatch.h"
53#define COMPONENT_UPP_SELECT_ROOT()     MovieImport
54
55extern "C" {
56#if __MACH__
57        #include <CoreServices/Components.k.h>
58        #include <QuickTime/QuickTimeComponents.k.h>
59        #include <QuickTime/ComponentDispatchHelper.c>
60#else
61        #include <Components.k.h>
62        #include <QuickTimeComponents.k.h>
63        #include <ComponentDispatchHelper.c>
64#endif
65}
66
67#pragma mark-
68
69// Component Open Request - Required
70ComponentResult MatroskaImportOpen(MatroskaImport *store, ComponentInstance self)
71{
72        // Allocate memory for our globals, and inform the component manager that we've done so
73        store = new MatroskaImport(self);
74       
75        if (store == NULL)
76                return memFullErr;
77
78        SetComponentInstanceStorage(self, (Handle)store);
79       
80        return noErr;
81}
82
83// Component Close Request - Required
84ComponentResult MatroskaImportClose(MatroskaImport *store, ComponentInstance self)
85{
86        // Make sure to dealocate our storage
87        delete store;
88       
89        return noErr;
90}
91
92// Component Version Request - Required
93ComponentResult MatroskaImportVersion(MatroskaImport *store)
94{
95        return kMatroskaImportVersion;
96}
97
98#pragma mark-
99
100// MovieImportFile
101ComponentResult MatroskaImportFile(MatroskaImport *store, const FSSpec *theFile, Movie theMovie, Track targetTrack, 
102                                                                   Track *usedTrack, TimeValue atTime, TimeValue *durationAdded, long inFlags, long *outFlags)
103{
104        OSErr err = noErr;
105        AliasHandle alias = NULL;
106    FSRef theFileFSRef;
107
108        *outFlags = 0;
109
110    err = FSpMakeFSRef(theFile, &theFileFSRef);
111        if (err) goto bail;
112       
113        err = FSNewAliasMinimal(&theFileFSRef, &alias);
114        if (err) goto bail;
115       
116        err = store->ImportDataRef((Handle)alias, rAliasType, theMovie, targetTrack,
117                                                           usedTrack, atTime, durationAdded, inFlags, outFlags);
118        if (err) goto bail;
119       
120        LoadExternalSubtitles(&theFileFSRef, theMovie);
121       
122bail:
123        if (alias)
124                DisposeHandle((Handle)alias);
125
126        return err;
127}
128
129// MovieImportDataRef
130ComponentResult MatroskaImportDataRef(MatroskaImport *store, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
131                                                                          Track *usedTrack, TimeValue atTime, TimeValue *durationAdded, long inFlags, long *outFlags)
132{
133        return store->ImportDataRef(dataRef, dataRefType, theMovie, targetTrack,
134                                                                usedTrack, atTime, durationAdded, inFlags, outFlags);
135}
136
137// MovieImportValidate
138ComponentResult MatroskaImportValidate(MatroskaImport *store, const FSSpec *theFile, Handle theData, Boolean *valid)
139{
140        OSErr err = noErr;
141        AliasHandle alias = NULL; 
142        FSRef theFileFSRef;
143       
144        *valid = false;
145       
146    err = FSpMakeFSRef(theFile, &theFileFSRef);
147        if (err) goto bail;
148       
149        err = FSNewAliasMinimal(&theFileFSRef, &alias);
150        if (err) goto bail;
151       
152        err = store->ValidateDataRef((Handle)alias, rAliasType, (UInt8 *)valid);
153
154bail:
155        if (alias)
156                DisposeHandle((Handle)alias);
157
158        return err;
159}
160
161// MovieImportGetMIMETypeList
162ComponentResult MatroskaImportGetMIMETypeList(MatroskaImport *store, QTAtomContainer *retMimeInfo)
163{
164        // Note that GetComponentResource is only available in QuickTime 3.0 or later.
165        // However, it's safe to call it here because GetMIMETypeList is only defined in QuickTime 3.0 or later.
166        return GetComponentResource((Component)store->Component(), 'mime', 510, (Handle *)retMimeInfo);
167}
168
169// MovieImportValidateDataRef
170ComponentResult MatroskaImportValidateDataRef(MatroskaImport *store, Handle dataRef, OSType dataRefType, UInt8 *valid)
171{
172        return store->ValidateDataRef(dataRef, dataRefType, valid);
173}
174
175ComponentResult MatroskaImportIdle(MatroskaImport *store, long inFlags, long *outFlags)
176{
177        return store->Idle(inFlags, outFlags);
178}
179
180ComponentResult MatroskaImportSetIdleManager(MatroskaImport *store, IdleManager im)
181{
182        return store->SetIdleManager(im);
183}
184
185ComponentResult MatroskaImportGetMaxLoadedTime(MatroskaImport *store, TimeValue *time)
186{
187        return store->GetMaxLoadedTime(time);
188}
189
190ComponentResult MatroskaImportGetLoadState(MatroskaImport *store, long *importerLoadState)
191{
192        return store->GetLoadState(importerLoadState);
193}
194
195
196MatroskaImport::MatroskaImport(ComponentInstance self)
197{
198        this->self = self;
199        dataRef = NULL;
200        dataRefType = 0;
201        theMovie = NULL;
202        chapterTrack = NULL;
203        baseTrack = NULL;
204        timecodeScale = 1000000;
205        movieDuration = 0;
206        idleManager = NULL;
207        loadState = kMovieLoadStateLoading;
208        ioHandler = NULL;
209        aStream = NULL;
210        el_l0 = NULL;
211        el_l1 = NULL;
212}
213
214MatroskaImport::~MatroskaImport()
215{
216        if (el_l1)
217                delete el_l1;
218       
219        if (el_l0)
220                delete el_l0;
221       
222        if (aStream)
223                delete aStream;
224       
225        if (ioHandler)
226                delete ioHandler;
227}
228
229ComponentResult MatroskaImport::ImportDataRef(Handle dataRef, OSType dataRefType, Movie theMovie,
230                                                                                          Track targetTrack, Track *usedTrack,
231                                                                                          TimeValue atTime, TimeValue *durationAdded,
232                                                                                          long inFlags, long *outFlags)
233{
234        ComponentResult err = noErr;
235        this->dataRef = dataRef;
236        this->dataRefType = dataRefType;
237        this->theMovie = theMovie;
238       
239        *outFlags = 0;
240       
241        if (inFlags & movieImportMustUseTrack)
242                return paramErr;
243       
244        loadState = kMovieLoadStateLoading;
245       
246        try {
247                if (!OpenFile())
248                        // invalid file, validate should catch this
249                        return invalidMovie;
250               
251                SetupMovie();
252               
253                // SetupMovie() couldn't find any level one elements, so nothing to import
254                if (el_l1 == NULL)
255                        return noErr;
256               
257                if (inFlags & movieImportWithIdle) {
258                        create_placeholder_track(theMovie, &baseTrack, movieDuration, dataRef, dataRefType);
259                       
260                        // try to import one cluster so we have at least something
261                        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
262                                int upperLevel = 0;
263                                EbmlElement *dummyElt = NULL;
264                               
265                                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
266                                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
267                               
268                                ImportCluster(cluster, true);
269                        }
270                       
271                        if (!NextLevel1Element())
272                                *outFlags |= movieImportResultComplete;
273                        else {
274                                *outFlags |= movieImportResultNeedIdles;
275                                return noErr;
276                        }
277                       
278                } else 
279                do {
280                        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
281                                int upperLevel = 0;
282                                EbmlElement *dummyElt = NULL;
283                               
284                                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
285                                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
286                               
287                                ImportCluster(cluster, false);
288                        }
289                } while (NextLevel1Element());
290               
291                // insert the a/v tracks' samples
292                for (int i = 0; i < tracks.size(); i++)
293                        tracks[i].FinishTrack();
294               
295        } catch (CRTError &err) {
296                return err.getError();
297        }
298       
299        loadState = kMovieLoadStateComplete;
300       
301        return noErr;
302}
303
304ComponentResult MatroskaImport::ValidateDataRef(Handle dataRef, OSType dataRefType, UInt8 *valid)
305{
306        this->dataRef = dataRef;
307        this->dataRefType = dataRefType;
308       
309        *valid = 0;
310       
311        try {
312                if (OpenFile())
313                        *valid = 128;
314        } catch (CRTError &err) {
315                return err.getError();
316        }
317       
318        return noErr;
319}
320
321ComponentResult MatroskaImport::Idle(long inFlags, long *outFlags)
322{
323        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
324                int upperLevel = 0;
325                EbmlElement *dummyElt = NULL;
326               
327                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
328                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
329               
330                ImportCluster(cluster, true);
331        }
332       
333        if (!NextLevel1Element()) {
334                if (baseTrack)
335                        DisposeMovieTrack(baseTrack);
336                *outFlags |= movieImportResultComplete;
337                loadState = kMovieLoadStateComplete;
338               
339                for (int i = 0; i < tracks.size(); i++)
340                        tracks[i].FinishTrack();
341        }
342       
343        return noErr;
344}
345
346ComponentResult MatroskaImport::SetIdleManager(IdleManager im)
347{
348        idleManager = im;
349        return noErr;
350}
351
352ComponentResult MatroskaImport::GetMaxLoadedTime(TimeValue *time)
353{
354        *time = 0;
355        for (int i = 0; i < tracks.size(); i++) {
356                if (tracks[i].maxLoadedTime > *time)
357                        *time = tracks[i].maxLoadedTime;
358        }
359        return noErr;
360}
361
362ComponentResult MatroskaImport::GetLoadState(long *importerLoadState)
363{
364        *importerLoadState = loadState;
365        return noErr;
366}
Note: See TracBrowser for help on using the repository browser.