source: trunk/MatroskaImport.cpp @ 1160

Revision 1160, 10.2 KB checked in by gbooker, 5 years ago (diff)

Consolidate Resource Version numbers
Fixes #415

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 "PerianResourceIDs.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               
119bail:
120        if (alias)
121                DisposeHandle((Handle)alias);
122
123        return err;
124}
125
126// MovieImportDataRef
127ComponentResult MatroskaImportDataRef(MatroskaImport *store, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
128                                                                          Track *usedTrack, TimeValue atTime, TimeValue *durationAdded, long inFlags, long *outFlags)
129{
130        ComponentResult res = store->ImportDataRef(dataRef, dataRefType, theMovie, targetTrack,
131                                                                usedTrack, atTime, durationAdded, inFlags, outFlags);
132       
133        LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);
134        return res;
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        lastIdleTime = 0;
209        idlesSinceLastAdd = 0;
210        ioHandler = NULL;
211        aStream = NULL;
212        el_l0 = NULL;
213        el_l1 = NULL;
214        segmentOffset = 0;
215        seenInfo = false;
216        seenTracks = false;
217        seenChapters = false;
218}
219
220MatroskaImport::~MatroskaImport()
221{
222        if (el_l1)
223                delete el_l1;
224       
225        if (el_l0)
226                delete el_l0;
227       
228        if (aStream)
229                delete aStream;
230       
231        if (ioHandler)
232                delete ioHandler;
233}
234
235ComponentResult MatroskaImport::ImportDataRef(Handle dataRef, OSType dataRefType, Movie theMovie,
236                                                                                          Track targetTrack, Track *usedTrack,
237                                                                                          TimeValue atTime, TimeValue *durationAdded,
238                                                                                          long inFlags, long *outFlags)
239{
240        ComponentResult err = noErr;
241        this->dataRef = dataRef;
242        this->dataRefType = dataRefType;
243        this->theMovie = theMovie;
244       
245        *outFlags = 0;
246       
247        if (inFlags & movieImportMustUseTrack)
248                return paramErr;
249       
250        loadState = kMovieLoadStateLoading;
251       
252        try {
253                if (!OpenFile())
254                        // invalid file, validate should catch this
255                        return invalidMovie;
256                               
257                err = SetupMovie();
258                if (err) return err;
259               
260                // SetupMovie() couldn't find any level one elements, so nothing to import
261                if (el_l1 == NULL)
262                        return noErr;
263               
264                if (inFlags & movieImportWithIdle) {
265                        create_placeholder_track(theMovie, &baseTrack, movieDuration, dataRef, dataRefType);
266                       
267                        // try to import one cluster so we have at least something
268                        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
269                                int upperLevel = 0;
270                                EbmlElement *dummyElt = NULL;
271                               
272                                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
273                                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
274                               
275                                ImportCluster(cluster, true);
276                        }
277                       
278                        if (!NextLevel1Element()) {
279                                *outFlags |= movieImportResultComplete;
280
281                                for (int i = 0; i < tracks.size(); i++)
282                                        tracks[i].FinishTrack();
283                        }
284                        else {
285                                *outFlags |= movieImportResultNeedIdles;
286                        }
287                       
288                        return noErr;
289                       
290                }
291                do {
292                        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
293                                int upperLevel = 0;
294                                EbmlElement *dummyElt = NULL;
295                               
296                                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
297                                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
298                               
299                                ImportCluster(cluster, false);
300                        }
301                } while (NextLevel1Element());
302               
303                // insert the a/v tracks' samples
304                for (int i = 0; i < tracks.size(); i++)
305                        tracks[i].FinishTrack();
306               
307        } catch (CRTError &err) {
308                return err.getError();
309        }
310       
311        loadState = kMovieLoadStateComplete;
312       
313        return noErr;
314}
315
316ComponentResult MatroskaImport::ValidateDataRef(Handle dataRef, OSType dataRefType, UInt8 *valid)
317{
318        this->dataRef = dataRef;
319        this->dataRefType = dataRefType;
320       
321        *valid = 0;
322       
323        try {
324                if (OpenFile())
325                        *valid = 128;
326        } catch (CRTError &err) {
327                return err.getError();
328        }
329       
330        return noErr;
331}
332
333ComponentResult MatroskaImport::Idle(long inFlags, long *outFlags)
334{
335        TimeValue currentIdleTime = GetMovieTime(theMovie, NULL);
336        TimeValue maxLoadedTime;
337        GetMaxLoadedTime(&maxLoadedTime);
338        TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
339       
340        idlesSinceLastAdd++;
341       
342        if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
343                int upperLevel = 0;
344                EbmlElement *dummyElt = NULL;
345               
346                el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
347                KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
348               
349                // Only add samples every 20 idles when paused
350                // or if we're playing and only have 5 seconds away from the end of
351                // what's already been added
352                if (currentIdleTime == lastIdleTime && idlesSinceLastAdd > 20 || 
353                        maxLoadedTime < currentIdleTime + 5*movieTimeScale)
354                {
355                        idlesSinceLastAdd = 0;
356                        ImportCluster(cluster, true);
357                }
358                else
359                        ImportCluster(cluster, false);
360        }
361       
362        if (!NextLevel1Element()) {
363                if (baseTrack)
364                        DisposeMovieTrack(baseTrack);
365                *outFlags |= movieImportResultComplete;
366                loadState = kMovieLoadStateComplete;
367               
368                for (int i = 0; i < tracks.size(); i++)
369                        tracks[i].FinishTrack();
370        }
371       
372        lastIdleTime = currentIdleTime;
373        return noErr;
374}
375
376ComponentResult MatroskaImport::SetIdleManager(IdleManager im)
377{
378        idleManager = im;
379        return noErr;
380}
381
382ComponentResult MatroskaImport::GetMaxLoadedTime(TimeValue *time)
383{
384        *time = 0;
385        for (int i = 0; i < tracks.size(); i++) {
386                if (tracks[i].maxLoadedTime > *time)
387                        *time = tracks[i].maxLoadedTime;
388        }
389        return noErr;
390}
391
392ComponentResult MatroskaImport::GetLoadState(long *importerLoadState)
393{
394        *importerLoadState = loadState;
395        return noErr;
396}
Note: See TracBrowser for help on using the repository browser.