Files
matroska/src/FileKax.cpp

466 lines
11 KiB
C++

/****************************************************************************
** libmatroska : parse Matroska files, see http://www.matroska.org/
**
** <file/class description>
**
** Copyright (C) 2002-2003 Steve Lhomme. All rights reserved.
**
** This file is part of libmatroska.
**
** This library 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; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library 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 library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**
** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.**
** Contact license@matroska.org if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
/*!
\file
\version \$Id: FileKax.cpp 640 2004-07-09 21:05:36Z mosu $
\author Steve Lhomme <robux4 @ users.sf.net>
*/
//#include "StdInclude.h"
#include "matroska/FileKax.h"
#include "ebml/EbmlVersion.h"
#include "ebml/EbmlContexts.h"
//#include "Cluster.h"
//#include "Track.h"
//#include "Block.h"
//#include "Frame.h"
//#include "Version.h"
START_LIBMATROSKA_NAMESPACE
//typedef Track *TrackID;
FileMatroska::FileMatroska(IOCallback & output)
:myFile(output)
#ifdef OLD
,myCurrReadBlock(NULL)
,myMinClusterSize(5*1024) // 5KB is the min size of a cluster
,myMaxClusterSize(2*1024*1024) // 2MB is the max size of a cluster
,myCurrReadBlockTrack(0)
,myCurrWriteCluster(2*1024*1024) // myMaxClusterSize
,myCurrReadCluster(NULL)
,myReadBlockNumber(0)
#endif // OLD
{
#ifdef OLD
myStreamInfo.MainHeaderSize = TypeHeader::default_size() +
ActualHeader::default_size() +
ExtendedInfo::default_size() +
ContentInfo::default_size();
myStreamInfo.TrackEntrySize = Track::default_size();
myStreamInfo.BlockHeadSize = BLOCK_HEADER_SIZE;
myStreamInfo.ClusterHeadSize = CLUSTER_HEADER_SIZE;
myStreamInfo.ClusterFootSize = CLUSTER_TRAILER_SIZE;
#endif // OLD
}
FileMatroska::~FileMatroska()
{
// if (myCurrCluster != NULL)
// throw 0; // there are some data left to write
// if (myCurrReadCluster != NULL || myCurrReadBlock != NULL)
// throw 0; // there are some data left to write
}
#ifdef OLD
void FileMatroska::SetMaxClusterSize(const uint32 value)
{
myMaxClusterSize = value;
myCurrWriteCluster.setMaxSize(value);
}
void FileMatroska::Close(const uint32 aTimeLength)
{
Flush();
// get the file size
myFile.setFilePointer(0,seek_end);
myMainHeader.type_SetSize(myFile.getFilePointer());
// rewrite the header at the beginning
myFile.setFilePointer(0,seek_beginning);
// get the Track-entry size
uint32 track_entries_size = 0;
for (size_t i=0; i<myTracks.size(); i++)
{
track_entries_size += myTracks[i]->default_size();
}
myStreamInfo.TrackEntriesSize = track_entries_size;
myStreamInfo.TimeLength = aTimeLength;
myMainHeader.Render(myFile, myStreamInfo);
for (i=0; i<myTracks.size(); i++)
{
delete myTracks[i];
}
}
/*!
\warning after rendering the head, some parameters are locked
*/
filepos_t FileMatroska::RenderHead(const std::string & aEncoderApp)
{
try {
uint32 track_entries_size = 0;
for (size_t i=0; i<myTracks.size(); i++)
{
track_entries_size += myTracks[i]->default_size();
}
std::string aStr = LIB_NAME;
aStr += " ";
aStr += VERSION;
myStreamInfo.EncoderLib = aStr;
myStreamInfo.EncoderApp = aEncoderApp;
myStreamInfo.TrackEntryPosition = 0 + myStreamInfo.MainHeaderSize;
myStreamInfo.TrackEntriesSize = myTracks.size() * myStreamInfo.TrackEntrySize;
myStreamInfo.CodecEntryPosition = myStreamInfo.MainHeaderSize + myStreamInfo.TrackEntriesSize;
myStreamInfo.CodecEntrySize = 4;
for (i=0; i<myTracks.size(); i++)
{
myStreamInfo.CodecEntrySize += myTracks[i]->CodecSize();
}
// Main Header
filepos_t result = myMainHeader.Render(myFile, myStreamInfo);
// Track Entries
for (i=0; i<myTracks.size(); i++)
{
myTracks[i]->RenderEntry(myFile, i+1);
}
myStreamInfo.ClusterPosition = myStreamInfo.CodecEntryPosition + myStreamInfo.CodecEntrySize;
// Codec Header
result = CodecHeader::Render(myFile, myTracks);
return result;
}
catch (exception & Ex)
{
throw Ex;
}
}
/*!
\return 0 if the track was not created, or a valid track number
*/
Track * FileMatroska::CreateTrack(const track_type aType)
{
myTracks.push_back(new Track(aType));
return myTracks.back();
}
/*Track *FileMatroska::findTrack(Track * aTrack) const
{
for (size_t i=0; i<myTracks.size(); i++)
{
if (myTracks[i] == aTrack)
return myTracks[i];
}
return NULL;
}*/
void FileMatroska::track_SetName(Track * aTrack, const std::string & aName)
{
if (IsMyTrack(aTrack))
{
aTrack->SetName(aName);
}
}
void FileMatroska::track_SetLaced(Track * aTrack, bool bLaced)
{
if (IsMyTrack(aTrack))
{
aTrack->SetLaced(bLaced);
}
}
bool FileMatroska::AddFrame(Track * aTrack, const uint32 aTimecode, const binary *aFrame, const uint32 aFrameSize,
bool aKeyFrame, bool aBFrame)
{
try {
// make sure we know that track
if (IsMyTrack(aTrack))
{
// pass the cluster to the track
// handle the creation of a new cluster if needed
if (aTrack->AddFrame(aTimecode, aFrame, aFrameSize, aKeyFrame, aBFrame))
{
while (!aTrack->SerialiseBlock(myCurrWriteCluster))
{
/// \todo handle errors
uint32 aNbBlock;
myStreamInfo.ClusterSize += myCurrWriteCluster.Render(myFile, aNbBlock);
myStreamInfo.NumberBlock += aNbBlock;
myCurrWriteCluster.Flush();
}
}
return true;
}
return false;
}
catch (exception & Ex)
{
throw Ex;
}
}
void FileMatroska::Flush()
{
uint32 aNbBlock;
myStreamInfo.ClusterSize += myCurrWriteCluster.Render(myFile,aNbBlock);
myStreamInfo.NumberBlock += aNbBlock;
}
uint32 FileMatroska::ReadHead()
{
try {
uint32 result = myMainHeader.Read(myFile, myStreamInfo);
return result;
}
catch (exception & Ex)
{
throw Ex;
}
}
uint32 FileMatroska::ReadTracks()
{
try {
uint32 result = 0;
// seek to the start of the Track Entries
myFile.setFilePointer(myStreamInfo.TrackEntryPosition);
// get the number of Track Entries
uint8 TrackNumber = myStreamInfo.TrackEntriesSize / myStreamInfo.TrackEntrySize;
// read all the Track Entries
myTracks.clear();
for (uint8 TrackIdx = 0; TrackIdx<TrackNumber; TrackIdx ++) {
Track * tmpTrack = Track::ReadEntry(myFile, TrackIdx+1, myStreamInfo);
if (tmpTrack == NULL)
throw 0;
myTracks.push_back(tmpTrack);
}
return result;
}
catch (exception & Ex)
{
throw Ex;
}
}
uint32 FileMatroska::ReadCodec()
{
try {
// seek to the start of the Track Entries
myFile.setFilePointer(myStreamInfo.CodecEntryPosition);
uint32 result = CodecHeader::Read(myFile, myTracks);
return result;
}
catch (exception & Ex)
{
throw Ex;
}
}
inline bool FileMatroska::IsMyTrack(const Track * aTrack) const
{
if (aTrack == 0)
throw 0;
for (std::vector<Track*>::const_iterator i = myTracks.begin(); i != myTracks.end(); i ++)
{
if (*i == aTrack)
break;
}
if (i != myTracks.end())
return true;
else
return false;
}
void FileMatroska::SelectReadingTrack(Track * aTrack, bool select)
{
if (IsMyTrack(aTrack))
{
// here we have the right track
// check if it's not already selected
for (std::vector<uint8>::iterator j = mySelectedTracks.begin();
j != mySelectedTracks.end(); j ++)
{
if (*j == aTrack->TrackNumber())
break;
}
if (select && j == mySelectedTracks.end())
mySelectedTracks.push_back(aTrack->TrackNumber());
else if (!select && j != mySelectedTracks.end())
mySelectedTracks.erase(j);
std::sort(mySelectedTracks.begin(), mySelectedTracks.end());
}
}
inline bool FileMatroska::IsReadingTrack(const uint8 aTrackNumber) const
{
for (std::vector<uint8>::const_iterator trackIdx = mySelectedTracks.begin();
trackIdx != mySelectedTracks.end() && *trackIdx < aTrackNumber;
trackIdx++)
{}
if (trackIdx == mySelectedTracks.end())
return false;
else
return true;
}
//
void FileMatroska::Track_GetInfo(const Track * aTrack, TrackInfo & aTrackInfo) const
{
if (IsMyTrack(aTrack))
{
aTrack->GetInfo(aTrackInfo);
}
}
// Audio related getters/setters
void FileMatroska::Track_GetInfo_Audio(const Track * aTrack, TrackInfoAudio & aTrackInfo) const
{
if (IsMyTrack(aTrack))
{
aTrack->GetInfoAudio(aTrackInfo);
}
}
void FileMatroska::Track_SetInfo_Audio(Track * aTrack, const TrackInfoAudio & aTrackInfo)
{
if (IsMyTrack(aTrack))
{
aTrack->SetInfoAudio(aTrackInfo);
}
}
// Video related getters/setters
void FileMatroska::Track_GetInfo_Video(const Track * aTrack, TrackInfoVideo & aTrackInfo) const
{
if (IsMyTrack(aTrack))
{
aTrack->GetInfoVideo(aTrackInfo);
}
}
void FileMatroska::Track_SetInfo_Video(Track * aTrack, const TrackInfoVideo & aTrackInfo)
{
if (IsMyTrack(aTrack))
{
aTrack->SetInfoVideo(aTrackInfo);
}
}
/*!
\todo exit when there is no Block left
*/
bool FileMatroska::ReadFrame(Track * & aTrack, uint32 & aTimecode, const binary * & aFrame, uint32 & aFrameSize,
bool & aKeyFrame, bool & aBFrame)
{
if (myCurrReadBlockTrack == 0)
{
do {
if (myReadBlockNumber >= myStreamInfo.NumberBlock)
{
// myReadBlockNumber = myStreamInfo.NumberBlock;
return false;
}
// get the next frame in the file
if (!myCurrReadCluster.BlockLeft())
{
myCurrReadCluster.Flush();
try {
myCurrReadCluster.FindHead(myFile);
}
catch (exception & Ex)
{
return false;
}
}
myCurrReadCluster.GetBlock( myCurrReadBlock, myCurrReadBlockSize, myCurrReadBlockTrack );
myReadBlockNumber++;
} while (!IsReadingTrack(myCurrReadBlockTrack));
// get the track associated (normally from myTracks)
aTrack = myTracks[myCurrReadBlockTrack-1];
// get the next frame from the current block
aTrack->HandleBlock(myCurrReadBlock, myCurrReadBlockSize);
}
else
{
// get the track associated (normally from myTracks)
aTrack = myTracks[myCurrReadBlockTrack-1];
}
Frame * myReadFrame;
aTrack->GetNextFrame(aTimecode, myReadFrame, aKeyFrame, aBFrame);
aFrame = myReadFrame->buf();
aFrameSize = myReadFrame->length();
if (aTrack->NoFrameLeft())
{
aTrack->FlushBlock();
myCurrReadBlockTrack = 0;
}
return true;
}
#endif // OLD
END_LIBMATROSKA_NAMESPACE
void matroska_init()
{
#if LIBEBML_VERSION >= 0x010000
ebml_init();
#endif
}
void matroska_done()
{
#if LIBEBML_VERSION >= 0x010000
ebml_done();
#endif
}