Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
/*
* MatroskaImport.cpp
*
* MatroskaImport.cpp - QuickTime importer interface for opening a Matroska file.
*
*
* Copyright (c) 2006 David Conrad
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <matroska/KaxSegment.h>
#include <matroska/KaxCluster.h>
#include <matroska/KaxBlock.h>
#include <ebml/StdIOCallback.h>
#include "PerianResourceIDs.h"
#include "MatroskaImport.h"
#include "SubImport.h"
#include "Codecprintf.h"
extern "C"
ComponentResult create_placeholder_track(Movie movie, Track *placeholderTrack, TimeValue duration, Handle dataRef, OSType dataRefType);
using namespace libmatroska;
#pragma mark- Component Dispatch
// Setup required for ComponentDispatchHelper.c since I don't feel like writing a dispatcher
// for a C++ class akin to Apple's ACCodec.cpp classes for Core Audio codecs.
#define MOVIEIMPORT_BASENAME() MatroskaImport
#define MOVIEIMPORT_GLOBALS() MatroskaImport *storage
#define CALLCOMPONENT_BASENAME() MOVIEIMPORT_BASENAME()
#define CALLCOMPONENT_GLOBALS() MOVIEIMPORT_GLOBALS()
#define COMPONENT_DISPATCH_FILE "MatroskaImportDispatch.h"
#define COMPONENT_UPP_SELECT_ROOT() MovieImport
extern "C" {
#include <CoreServices/Components.k.h>
#include <QuickTime/QuickTimeComponents.k.h>
#pragma GCC visibility push(default)
#include <QuickTime/ComponentDispatchHelper.c>
#pragma GCC visibility pop
}
#pragma mark-
// Component Open Request - Required
ComponentResult MatroskaImportOpen(MatroskaImport *store, ComponentInstance self)
{
// Allocate memory for our globals, and inform the component manager that we've done so
store = new MatroskaImport(self);
if (store == NULL)
return memFullErr;
SetComponentInstanceStorage(self, (Handle)store);
return noErr;
}
// Component Close Request - Required
ComponentResult MatroskaImportClose(MatroskaImport *store, ComponentInstance self)
{
// Make sure to dealocate our storage
delete store;
return noErr;
}
// Component Version Request - Required
ComponentResult MatroskaImportVersion(MatroskaImport *store)
{
return kMatroskaImportVersion;
}
#pragma mark-
// MovieImportFile
ComponentResult MatroskaImportFile(MatroskaImport *store, const FSSpec *theFile, Movie theMovie, Track targetTrack,
Track *usedTrack, TimeValue atTime, TimeValue *durationAdded, long inFlags, long *outFlags)
{
OSErr err = noErr;
AliasHandle alias = NULL;
FSRef theFileFSRef;
*outFlags = 0;
err = FSpMakeFSRef(theFile, &theFileFSRef);
if (err) goto bail;
err = FSNewAliasMinimal(&theFileFSRef, &alias);
if (err) goto bail;
err = store->ImportDataRef((Handle)alias, rAliasType, theMovie, targetTrack,
usedTrack, atTime, durationAdded, inFlags, outFlags);
bail:
if (alias)
DisposeHandle((Handle)alias);
return err;
}
// MovieImportDataRef
ComponentResult MatroskaImportDataRef(MatroskaImport *store, Handle dataRef, OSType dataRefType, Movie theMovie, Track targetTrack,
Track *usedTrack, TimeValue atTime, TimeValue *durationAdded, long inFlags, long *outFlags)
{
ComponentResult res = store->ImportDataRef(dataRef, dataRefType, theMovie, targetTrack,
usedTrack, atTime, durationAdded, inFlags, outFlags);
LoadExternalSubtitlesFromFileDataRef(dataRef, dataRefType, theMovie);
return res;
}
// MovieImportValidate
ComponentResult MatroskaImportValidate(MatroskaImport *store, const FSSpec *theFile, Handle theData, Boolean *valid)
{
OSErr err = noErr;
AliasHandle alias = NULL;
FSRef theFileFSRef;
*valid = false;
err = FSpMakeFSRef(theFile, &theFileFSRef);
if (err) goto bail;
err = FSNewAliasMinimal(&theFileFSRef, &alias);
if (err) goto bail;
err = store->ValidateDataRef((Handle)alias, rAliasType, (UInt8 *)valid);
bail:
if (alias)
DisposeHandle((Handle)alias);
return err;
}
// MovieImportGetMIMETypeList
ComponentResult MatroskaImportGetMIMETypeList(MatroskaImport *store, QTAtomContainer *retMimeInfo)
{
// Note that GetComponentResource is only available in QuickTime 3.0 or later.
// However, it's safe to call it here because GetMIMETypeList is only defined in QuickTime 3.0 or later.
return GetComponentResource((Component)store->Component(), 'mime', 510, (Handle *)retMimeInfo);
}
// MovieImportValidateDataRef
ComponentResult MatroskaImportValidateDataRef(MatroskaImport *store, Handle dataRef, OSType dataRefType, UInt8 *valid)
{
return store->ValidateDataRef(dataRef, dataRefType, valid);
}
ComponentResult MatroskaImportIdle(MatroskaImport *store, long inFlags, long *outFlags)
{
return store->Idle(inFlags, outFlags);
}
ComponentResult MatroskaImportSetIdleManager(MatroskaImport *store, IdleManager im)
{
return store->SetIdleManager(im);
}
ComponentResult MatroskaImportGetMaxLoadedTime(MatroskaImport *store, TimeValue *time)
{
return store->GetMaxLoadedTime(time);
}
ComponentResult MatroskaImportGetLoadState(MatroskaImport *store, long *importerLoadState)
{
return store->GetLoadState(importerLoadState);
}
MatroskaImport::MatroskaImport(ComponentInstance self)
{
this->self = self;
dataRef = NULL;
dataRefType = 0;
theMovie = NULL;
chapterTrack = NULL;
baseTrack = NULL;
timecodeScale = 1000000;
movieDuration = 0;
idleManager = NULL;
loadState = kMovieLoadStateLoading;
lastIdleTime = 0;
idlesSinceLastAdd = 0;
ioHandler = NULL;
aStream = NULL;
el_l0 = NULL;
el_l1 = NULL;
segmentOffset = 0;
seenInfo = false;
seenTracks = false;
seenChapters = false;
}
MatroskaImport::~MatroskaImport()
{
if (el_l1)
delete el_l1;
if (el_l0)
delete el_l0;
if (aStream)
delete aStream;
if (ioHandler)
delete ioHandler;
}
ComponentResult MatroskaImport::ImportDataRef(Handle dataRef, OSType dataRefType, Movie theMovie,
Track targetTrack, Track *usedTrack,
TimeValue atTime, TimeValue *durationAdded,
long inFlags, long *outFlags)
{
ComponentResult err = noErr;
this->dataRef = dataRef;
this->dataRefType = dataRefType;
this->theMovie = theMovie;
*outFlags = 0;
if (inFlags & movieImportMustUseTrack)
return paramErr;
loadState = kMovieLoadStateLoading;
try {
if (!OpenFile())
// invalid file, validate should catch this
return invalidMovie;
err = SetupMovie();
if (err) return err;
// SetupMovie() couldn't find any level one elements, so nothing to import
if (el_l1 == NULL)
return noErr;
if (inFlags & movieImportWithIdle) {
create_placeholder_track(theMovie, &baseTrack, movieDuration, dataRef, dataRefType);
// try to import one cluster so we have at least something
if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
int upperLevel = 0;
EbmlElement *dummyElt = NULL;
el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
ImportCluster(cluster, true);
}
if (!NextLevel1Element()) {
*outFlags |= movieImportResultComplete;
for (int i = 0; i < tracks.size(); i++)
tracks[i].FinishTrack();
}
else {
*outFlags |= movieImportResultNeedIdles;
}
return noErr;
}
do {
if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
int upperLevel = 0;
EbmlElement *dummyElt = NULL;
el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
ImportCluster(cluster, false);
}
} while (NextLevel1Element());
// insert the a/v tracks' samples
for (int i = 0; i < tracks.size(); i++)
tracks[i].FinishTrack();
} catch (CRTError &err) {
return err.getError();
}
loadState = kMovieLoadStateComplete;
return noErr;
}
ComponentResult MatroskaImport::ValidateDataRef(Handle dataRef, OSType dataRefType, UInt8 *valid)
{
this->dataRef = dataRef;
this->dataRefType = dataRefType;
*valid = 0;
try {
if (OpenFile())
*valid = 128;
} catch (CRTError &err) {
return err.getError();
}
return noErr;
}
ComponentResult MatroskaImport::Idle(long inFlags, long *outFlags)
{
TimeValue currentIdleTime = GetMovieTime(theMovie, NULL);
TimeValue maxLoadedTime;
GetMaxLoadedTime(&maxLoadedTime);
TimeScale movieTimeScale = GetMovieTimeScale(theMovie);
ComponentResult err = noErr;
idlesSinceLastAdd++;
try {
if (EbmlId(*el_l1) == KaxCluster::ClassInfos.GlobalId) {
int upperLevel = 0;
EbmlElement *dummyElt = NULL;
el_l1->Read(*aStream, KaxCluster::ClassInfos.Context, upperLevel, dummyElt, true);
KaxCluster & cluster = *static_cast<KaxCluster *>(el_l1);
// Only add samples every 20 idles when paused
// or if we're playing and only have 5 seconds away from the end of
// what's already been added
if ((currentIdleTime == lastIdleTime && idlesSinceLastAdd > 20) ||
maxLoadedTime < currentIdleTime + 5*movieTimeScale)
{
idlesSinceLastAdd = 0;
ImportCluster(cluster, true);
}
else
ImportCluster(cluster, false);
}
if (!NextLevel1Element())
goto finish;
} catch (CRTError &cerr) {
err = cerr.getError();
Codecprintf(NULL, "MKV: caught an error (%d): %s\n", (int)err, cerr.what());
goto finish;
}
lastIdleTime = currentIdleTime;
return noErr;
finish:
lastIdleTime = currentIdleTime;
if (baseTrack)
DisposeMovieTrack(baseTrack);
*outFlags |= movieImportResultComplete;
loadState = kMovieLoadStateComplete;
for (int i = 0; i < tracks.size(); i++)
tracks[i].FinishTrack();
return err;
}
ComponentResult MatroskaImport::SetIdleManager(IdleManager im)
{
idleManager = im;
return noErr;
}
ComponentResult MatroskaImport::GetMaxLoadedTime(TimeValue *time)
{
*time = 0;
for (int i = 0; i < tracks.size(); i++) {
if (tracks[i].maxLoadedTime > *time)
*time = tracks[i].maxLoadedTime;
}
return noErr;
}
ComponentResult MatroskaImport::GetLoadState(long *importerLoadState)
{
*importerLoadState = loadState;
return noErr;
}