source: trunk/MatroskaImport.cpp @ 334

Revision 334, 9.3 KB checked in by astrange, 8 years ago (diff)

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 *
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.