-/****************************************************************************\r
-** libmatroska : parse Matroska files, see http://www.matroska.org/\r
-**\r
-** <file/class description>\r
-**\r
-** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved.\r
-**\r
-** This library is free software; you can redistribute it and/or\r
-** modify it under the terms of the GNU Lesser General Public\r
-** License as published by the Free Software Foundation; either\r
-** version 2.1 of the License, or (at your option) any later version.\r
-** \r
-** This library is distributed in the hope that it will be useful,\r
-** but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
-** Lesser General Public License for more details.\r
-** \r
-** You should have received a copy of the GNU Lesser General Public\r
-** License along with this library; if not, write to the Free Software\r
-** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-**\r
-** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.**\r
-** Contact license@matroska.org if any conditions of this licensing are\r
-** not clear to you.\r
-**\r
-**********************************************************************/\r
-\r
-/*!\r
- \file\r
- \version \$Id: KaxBlock.cpp 1265 2007-01-14 17:20:35Z mosu $\r
- \author Steve Lhomme <robux4 @ users.sf.net>\r
- \author Julien Coloos <suiryc @ users.sf.net>\r
-*/\r
-#include <cassert>\r
-\r
-//#include <streams.h>\r
-\r
-#include "matroska/KaxBlock.h"\r
-#include "matroska/KaxContexts.h"\r
-#include "matroska/KaxBlockData.h"\r
-#include "matroska/KaxCluster.h"\r
-#include "matroska/KaxDefines.h"\r
-\r
-START_LIBMATROSKA_NAMESPACE\r
-\r
-DEFINE_START_SEMANTIC(KaxBlockGroup)\r
-DEFINE_SEMANTIC_ITEM(true, true, KaxBlock)\r
-#if MATROSKA_VERSION >= 2\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxBlockVirtual)\r
-#endif // MATROSKA_VERSION\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxBlockDuration)\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxSlices)\r
-DEFINE_SEMANTIC_ITEM(true, true, KaxReferencePriority)\r
-DEFINE_SEMANTIC_ITEM(false, false, KaxReferenceBlock)\r
-#if MATROSKA_VERSION >= 2\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxReferenceVirtual)\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxCodecState)\r
-#endif // MATROSKA_VERSION\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxBlockAdditions)\r
-DEFINE_END_SEMANTIC(KaxBlockGroup)\r
-\r
-DEFINE_START_SEMANTIC(KaxBlockAdditions)\r
-DEFINE_SEMANTIC_ITEM(true, false, KaxBlockMore)\r
-DEFINE_END_SEMANTIC(KaxBlockAdditions)\r
-\r
-DEFINE_START_SEMANTIC(KaxBlockMore)\r
-DEFINE_SEMANTIC_ITEM(true, true, KaxBlockAddID)\r
-DEFINE_SEMANTIC_ITEM(true, true, KaxBlockAdditional)\r
-DEFINE_END_SEMANTIC(KaxBlockMore)\r
-\r
-DEFINE_MKX_MASTER_CONS (KaxBlockGroup, 0xA0, 1, KaxCluster, "BlockGroup");\r
-DEFINE_MKX_BINARY_CONS (KaxBlock, 0xA1, 1, KaxBlockGroup, "Block");\r
-DEFINE_MKX_UINTEGER (KaxBlockDuration, 0x9B, 1, KaxBlockGroup, "BlockDuration");\r
-#if MATROSKA_VERSION >= 2\r
-DEFINE_MKX_BINARY_CONS (KaxSimpleBlock, 0xA3, 1, KaxCluster, "SimpleBlock");\r
-DEFINE_MKX_BINARY_CONS (KaxBlockVirtual, 0xA2, 1, KaxBlockGroup, "BlockVirtual");\r
-DEFINE_MKX_BINARY (KaxCodecState, 0xA4, 1, KaxBlockGroup, "CodecState");\r
-#endif\r
-DEFINE_MKX_MASTER (KaxBlockAdditions, 0x75A1, 2, KaxBlockGroup, "BlockAdditions");\r
-DEFINE_MKX_MASTER (KaxBlockMore, 0xA6, 1, KaxBlockAdditions, "BlockMore");\r
-DEFINE_MKX_UINTEGER_DEF(KaxBlockAddID, 0xEE, 1, KaxBlockMore, "BlockAddID", 1);\r
-DEFINE_MKX_BINARY (KaxBlockAdditional, 0xA5, 1, KaxBlockMore, "BlockAdditional");\r
-\r
-\r
-DataBuffer * DataBuffer::Clone()\r
-{\r
- binary *ClonedData = (binary *)malloc(mySize * sizeof(binary));\r
- assert(ClonedData != NULL);\r
- memcpy(ClonedData, myBuffer ,mySize );\r
-\r
- SimpleDataBuffer * result = new SimpleDataBuffer(ClonedData, mySize, 0);\r
- result->bValidValue = bValidValue;\r
- return result;\r
-}\r
-\r
-SimpleDataBuffer::SimpleDataBuffer(const SimpleDataBuffer & ToClone)\r
- :DataBuffer((binary *)malloc(ToClone.mySize * sizeof(binary)), ToClone.mySize, myFreeBuffer)\r
-{\r
- assert(myBuffer != NULL);\r
- memcpy(myBuffer, ToClone.myBuffer ,mySize );\r
- bValidValue = ToClone.bValidValue;\r
-}\r
-\r
-bool KaxInternalBlock::ValidateSize() const\r
-{\r
- return (GetSize() >= 4); /// for the moment\r
-}\r
-\r
-KaxInternalBlock::~KaxInternalBlock()\r
-{\r
- ReleaseFrames();\r
-}\r
-\r
-KaxInternalBlock::KaxInternalBlock(const KaxInternalBlock & ElementToClone)\r
- :EbmlBinary(ElementToClone)\r
- ,myBuffers(ElementToClone.myBuffers.size())\r
- ,Timecode(ElementToClone.Timecode)\r
- ,LocalTimecode(ElementToClone.LocalTimecode)\r
- ,bLocalTimecodeUsed(ElementToClone.bLocalTimecodeUsed)\r
- ,TrackNumber(ElementToClone.TrackNumber)\r
- ,ParentCluster(ElementToClone.ParentCluster) ///< \todo not exactly\r
-{\r
- // add a clone of the list\r
- std::vector<DataBuffer *>::const_iterator Itr = ElementToClone.myBuffers.begin();\r
- std::vector<DataBuffer *>::iterator myItr = myBuffers.begin();\r
- while (Itr != ElementToClone.myBuffers.end())\r
- {\r
- *myItr = (*Itr)->Clone();\r
- Itr++; myItr++;\r
- }\r
-}\r
-\r
-\r
-KaxBlockGroup::~KaxBlockGroup()\r
-{\r
-//NOTE("KaxBlockGroup::~KaxBlockGroup");\r
-}\r
-\r
-KaxBlockGroup::KaxBlockGroup()\r
- :EbmlMaster(Context_KaxBlockGroup)\r
- ,ParentCluster(NULL)\r
- ,ParentTrack(NULL)\r
-{}\r
-\r
-/*!\r
- \todo handle flags\r
- \todo hardcoded limit of the number of frames in a lace should be a parameter\r
- \return true if more frames can be added to this Block\r
-*/\r
-bool KaxInternalBlock::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing, bool invisible)\r
-{\r
- SetValueIsSet();\r
- if (myBuffers.size() == 0) {\r
- // first frame\r
- Timecode = timecode;\r
- TrackNumber = track.TrackNumber();\r
- mInvisible = invisible;\r
- mLacing = lacing;\r
- }\r
- myBuffers.push_back(&buffer);\r
-\r
- // we don't allow more than 8 frames in a Block because the overhead improvement is minimal\r
- if (myBuffers.size() >= 8 || lacing == LACING_NONE)\r
- return false;\r
-\r
- if (lacing == LACING_XIPH)\r
- // decide wether a new frame can be added or not\r
- // a frame in a lace is not efficient when the place necessary to code it in a lace is bigger \r
- // than the size of a simple Block. That means more than 6 bytes (4 in struct + 2 for EBML) to code the size\r
- return (buffer.Size() < 6*0xFF);\r
- else\r
- return true;\r
-}\r
-\r
-/*!\r
- \return Returns the lacing type that produces the smallest footprint.\r
-*/\r
-LacingType KaxInternalBlock::GetBestLacingType() const {\r
- int XiphLacingSize, EbmlLacingSize, i;\r
- bool SameSize = true;\r
-\r
- if (myBuffers.size() <= 1)\r
- return LACING_NONE;\r
-\r
- XiphLacingSize = 1; // Number of laces is stored in 1 byte.\r
- EbmlLacingSize = 1;\r
- for (i = 0; i < (int)myBuffers.size() - 1; i++) {\r
- if (myBuffers[i]->Size() != myBuffers[i + 1]->Size())\r
- SameSize = false;\r
- XiphLacingSize += myBuffers[i]->Size() / 255 + 1;\r
- }\r
- EbmlLacingSize += CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize());\r
- for (i = 1; i < (int)myBuffers.size() - 1; i++)\r
- EbmlLacingSize += CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i - 1]->Size()), 0);\r
- if (SameSize)\r
- return LACING_FIXED;\r
- else if (XiphLacingSize < EbmlLacingSize)\r
- return LACING_XIPH;\r
- else\r
- return LACING_EBML;\r
-}\r
-\r
-filepos_t KaxInternalBlock::UpdateSize(bool bSaveDefault, bool bForceRender)\r
-{\r
- LacingType LacingHere;\r
- assert(EbmlBinary::GetBuffer() == NULL); // Data is not used for KaxInternalBlock\r
- assert(TrackNumber < 0x4000); // no more allowed for the moment\r
- unsigned int i;\r
-\r
- // compute the final size of the data\r
- switch (myBuffers.size()) {\r
- case 0:\r
- SetSize_(0);\r
- break;\r
- case 1:\r
- SetSize_(4 + myBuffers[0]->Size());\r
- break;\r
- default:\r
- SetSize_(4 + 1); // 1 for the lacing head\r
- if (mLacing == LACING_AUTO)\r
- LacingHere = GetBestLacingType();\r
- else\r
- LacingHere = mLacing;\r
- switch (LacingHere)\r
- {\r
- case LACING_XIPH:\r
- for (i=0; i<myBuffers.size()-1; i++) {\r
- SetSize_(GetSize() + myBuffers[i]->Size() + (myBuffers[i]->Size() / 0xFF + 1));\r
- }\r
- break;\r
- case LACING_EBML:\r
- SetSize_(GetSize() + myBuffers[0]->Size() + CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize()));\r
- for (i=1; i<myBuffers.size()-1; i++) {\r
- SetSize_(GetSize() + myBuffers[i]->Size() + CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size()), 0));\r
- }\r
- break;\r
- case LACING_FIXED:\r
- for (i=0; i<myBuffers.size()-1; i++) {\r
- SetSize_(GetSize() + myBuffers[i]->Size());\r
- }\r
- break;\r
- default:\r
- assert(0);\r
- }\r
- // Size of the last frame (not in lace)\r
- SetSize_(GetSize() + myBuffers[i]->Size());\r
- break;\r
- }\r
-\r
- if (TrackNumber >= 0x80)\r
- SetSize_(GetSize() + 1); // the size will be coded with one more octet\r
-\r
- return GetSize();\r
-}\r
-\r
-#if MATROSKA_VERSION >= 2\r
-KaxBlockVirtual::KaxBlockVirtual(const KaxBlockVirtual & ElementToClone)\r
- :EbmlBinary(ElementToClone)\r
- ,Timecode(ElementToClone.Timecode)\r
- ,TrackNumber(ElementToClone.TrackNumber)\r
- ,ParentCluster(ElementToClone.ParentCluster) ///< \todo not exactly\r
-{\r
- SetBuffer(DataBlock,sizeof(DataBlock));\r
- SetValueIsSet(false);\r
-}\r
-\r
-KaxBlockVirtual::KaxBlockVirtual()\r
-:ParentCluster(NULL)\r
-{\r
- SetBuffer(DataBlock,sizeof(DataBlock));\r
- SetValueIsSet(false);\r
-}\r
-\r
-KaxBlockVirtual::~KaxBlockVirtual()\r
-{\r
- if(GetBuffer() == DataBlock)\r
- SetBuffer( NULL, 0 ); \r
-}\r
-\r
-filepos_t KaxBlockVirtual::UpdateSize(bool bSaveDefault, bool bForceRender)\r
-{\r
- assert(TrackNumber < 0x4000);\r
- binary *cursor = EbmlBinary::GetBuffer();\r
- // fill data\r
- if (TrackNumber < 0x80) {\r
- *cursor++ = TrackNumber | 0x80; // set the first bit to 1 \r
- } else {\r
- *cursor++ = (TrackNumber >> 8) | 0x40; // set the second bit to 1\r
- *cursor++ = TrackNumber & 0xFF;\r
- }\r
-\r
- assert(ParentCluster != NULL);\r
- int16 ActualTimecode = ParentCluster->GetBlockLocalTimecode(Timecode);\r
- big_int16 b16(ActualTimecode);\r
- b16.Fill(cursor);\r
- cursor += 2;\r
-\r
- *cursor++ = 0; // flags\r
-\r
- return GetSize();\r
-}\r
-#endif // MATROSKA_VERSION\r
-\r
-/*!\r
- \todo more optimisation is possible (render the Block head and don't copy the buffer in memory, care should be taken with the allocation of Data)\r
- \todo the actual timecode to write should be retrieved from the Cluster from here\r
-*/\r
-filepos_t KaxInternalBlock::RenderData(IOCallback & output, bool bForceRender, bool bSaveDefault)\r
-{\r
- if (myBuffers.size() == 0) {\r
- return 0;\r
- } else {\r
- assert(TrackNumber < 0x4000);\r
- binary BlockHead[5], *cursor = BlockHead;\r
- unsigned int i;\r
-\r
- if (myBuffers.size() == 1) {\r
- SetSize_(4);\r
- mLacing = LACING_NONE;\r
- } else {\r
- if (mLacing == LACING_NONE)\r
- mLacing = LACING_EBML; // supposedly the best of all\r
- SetSize_(4 + 1); // 1 for the lacing head (number of laced elements)\r
- }\r
- if (TrackNumber > 0x80)\r
- SetSize_(GetSize() + 1);\r
-\r
- // write Block Head\r
- if (TrackNumber < 0x80) {\r
- *cursor++ = TrackNumber | 0x80; // set the first bit to 1 \r
- } else {\r
- *cursor++ = (TrackNumber >> 8) | 0x40; // set the second bit to 1\r
- *cursor++ = TrackNumber & 0xFF;\r
- }\r
-\r
- assert(ParentCluster != NULL);\r
- int16 ActualTimecode = ParentCluster->GetBlockLocalTimecode(Timecode);\r
- big_int16 b16(ActualTimecode);\r
- b16.Fill(cursor);\r
- cursor += 2;\r
-\r
- *cursor = 0; // flags\r
-\r
- if (mLacing == LACING_AUTO) {\r
- mLacing = GetBestLacingType();\r
- }\r
-\r
- // invisible flag\r
- if (mInvisible)\r
- *cursor = 0x08;\r
-\r
- if (bIsSimple) {\r
- if (bIsKeyframe)\r
- *cursor |= 0x80;\r
- if (bIsDiscardable)\r
- *cursor |= 0x01;\r
- }\r
- \r
- // lacing flag\r
- switch (mLacing)\r
- {\r
- case LACING_XIPH:\r
- *cursor++ |= 0x02;\r
- break;\r
- case LACING_EBML:\r
- *cursor++ |= 0x06;\r
- break;\r
- case LACING_FIXED:\r
- *cursor++ |= 0x04;\r
- break;\r
- case LACING_NONE:\r
- break;\r
- default:\r
- assert(0);\r
- }\r
-\r
- output.writeFully(BlockHead, 4 + ((TrackNumber > 0x80) ? 1 : 0));\r
-\r
- binary tmpValue;\r
- switch (mLacing)\r
- {\r
- case LACING_XIPH:\r
- // number of laces\r
- tmpValue = myBuffers.size()-1;\r
- output.writeFully(&tmpValue, 1);\r
-\r
- // set the size of each member in the lace\r
- for (i=0; i<myBuffers.size()-1; i++) {\r
- tmpValue = 0xFF;\r
- uint16 tmpSize = myBuffers[i]->Size();\r
- while (tmpSize >= 0xFF) {\r
- output.writeFully(&tmpValue, 1);\r
- SetSize_(GetSize() + 1);\r
- tmpSize -= 0xFF;\r
- }\r
- tmpValue = binary(tmpSize);\r
- output.writeFully(&tmpValue, 1);\r
- SetSize_(GetSize() + 1);\r
- }\r
- break;\r
- case LACING_EBML:\r
- // number of laces\r
- tmpValue = myBuffers.size()-1;\r
- output.writeFully(&tmpValue, 1);\r
-\r
- {\r
- int64 _Size;\r
- int _CodedSize;\r
- binary _FinalHead[8]; // 64 bits max coded size\r
-\r
- _Size = myBuffers[0]->Size();\r
-\r
- _CodedSize = CodedSizeLength(_Size, 0, IsFiniteSize());\r
-\r
- // first size in the lace is not a signed\r
- CodedValueLength(_Size, _CodedSize, _FinalHead);\r
- output.writeFully(_FinalHead, _CodedSize);\r
- SetSize_(GetSize() + _CodedSize);\r
-\r
- // set the size of each member in the lace\r
- for (i=1; i<myBuffers.size()-1; i++) {\r
- _Size = int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size());\r
- _CodedSize = CodedSizeLengthSigned(_Size, 0);\r
- CodedValueLengthSigned(_Size, _CodedSize, _FinalHead);\r
- output.writeFully(_FinalHead, _CodedSize);\r
- SetSize_(GetSize() + _CodedSize);\r
- }\r
- }\r
- break;\r
- case LACING_FIXED:\r
- // number of laces\r
- tmpValue = myBuffers.size()-1;\r
- output.writeFully(&tmpValue, 1);\r
- break;\r
- case LACING_NONE:\r
- break;\r
- default:\r
- assert(0);\r
- }\r
-\r
- // put the data of each frame\r
- for (i=0; i<myBuffers.size(); i++) {\r
- output.writeFully(myBuffers[i]->Buffer(), myBuffers[i]->Size());\r
- SetSize_(GetSize() + myBuffers[i]->Size());\r
- }\r
- }\r
-\r
- return GetSize();\r
-}\r
-\r
-uint64 KaxInternalBlock::ReadInternalHead(IOCallback & input)\r
-{\r
- binary Buffer[5], *cursor = Buffer;\r
- uint64 Result = input.read(cursor, 4);\r
- if (Result != 4)\r
- return Result;\r
- \r
- // update internal values\r
- TrackNumber = *cursor++;\r
- if ((TrackNumber & 0x80) == 0) {\r
- // there is extra data\r
- if ((TrackNumber & 0x40) == 0) {\r
- // We don't support track numbers that large !\r
- return Result;\r
- }\r
- Result += input.read(&Buffer[4], 1);\r
- TrackNumber = (TrackNumber & 0x3F) << 8;\r
- TrackNumber += *cursor++;\r
- } else {\r
- TrackNumber &= 0x7F;\r
- }\r
-\r
- \r
- big_int16 b16;\r
- b16.Eval(cursor);\r
- assert(ParentCluster != NULL);\r
- Timecode = ParentCluster->GetBlockGlobalTimecode(int16(b16));\r
- bLocalTimecodeUsed = false;\r
- cursor += 2;\r
-\r
- return Result;\r
-}\r
-\r
-/*!\r
- \todo better zero copy handling\r
-*/\r
-filepos_t KaxInternalBlock::ReadData(IOCallback & input, ScopeMode ReadFully)\r
-{\r
- filepos_t Result;\r
-\r
- FirstFrameLocation = input.getFilePointer(); // will be updated accordingly below\r
-\r
- if (ReadFully == SCOPE_ALL_DATA)\r
- {\r
- Result = EbmlBinary::ReadData(input, ReadFully);\r
- binary *cursor = EbmlBinary::GetBuffer();\r
- uint8 BlockHeadSize = 4;\r
-\r
- // update internal values\r
- TrackNumber = *cursor++;\r
- if ((TrackNumber & 0x80) == 0) {\r
- // there is extra data\r
- if ((TrackNumber & 0x40) == 0) {\r
- // We don't support track numbers that large !\r
- return Result;\r
- }\r
- TrackNumber = (TrackNumber & 0x3F) << 8;\r
- TrackNumber += *cursor++;\r
- BlockHeadSize++;\r
- } else {\r
- TrackNumber &= 0x7F;\r
- }\r
-\r
- big_int16 b16;\r
- b16.Eval(cursor);\r
- LocalTimecode = int16(b16);\r
- bLocalTimecodeUsed = true;\r
- cursor += 2;\r
-\r
- if (EbmlId(*this) == EBML_ID(KaxSimpleBlock)) {\r
- bIsKeyframe = (*cursor & 0x80) != 0;\r
- bIsDiscardable = (*cursor & 0x01) != 0;\r
- }\r
- mInvisible = (*cursor & 0x08) >> 3;\r
- mLacing = LacingType((*cursor++ & 0x06) >> 1);\r
-\r
- // put all Frames in the list\r
- if (mLacing == LACING_NONE) {\r
- FirstFrameLocation += cursor - EbmlBinary::GetBuffer();\r
- DataBuffer * soloFrame = new DataBuffer(cursor, GetSize() - BlockHeadSize);\r
- myBuffers.push_back(soloFrame);\r
- SizeList.resize(1);\r
- SizeList[0] = GetSize() - BlockHeadSize;\r
- } else {\r
- // read the number of frames in the lace\r
- uint32 LastBufferSize = GetSize() - BlockHeadSize - 1; // 1 for number of frame\r
- uint8 FrameNum = *cursor++; // number of frames in the lace - 1\r
- // read the list of frame sizes\r
- uint8 Index;\r
- int32 FrameSize;\r
- uint32 SizeRead;\r
- uint64 SizeUnknown;\r
-\r
- SizeList.resize(FrameNum + 1);\r
-\r
- switch (mLacing)\r
- {\r
- case LACING_XIPH:\r
- for (Index=0; Index<FrameNum; Index++) {\r
- // get the size of the frame\r
- FrameSize = 0;\r
- do {\r
- FrameSize += uint8(*cursor);\r
- LastBufferSize--;\r
- } while (*cursor++ == 0xFF);\r
- SizeList[Index] = FrameSize;\r
- LastBufferSize -= FrameSize;\r
- }\r
- SizeList[Index] = LastBufferSize;\r
- break;\r
- case LACING_EBML:\r
- SizeRead = LastBufferSize;\r
- FrameSize = ReadCodedSizeValue(cursor, SizeRead, SizeUnknown);\r
- SizeList[0] = FrameSize;\r
- cursor += SizeRead;\r
- LastBufferSize -= FrameSize + SizeRead;\r
-\r
- for (Index=1; Index<FrameNum; Index++) {\r
- // get the size of the frame\r
- SizeRead = LastBufferSize;\r
- FrameSize += ReadCodedSizeSignedValue(cursor, SizeRead, SizeUnknown);\r
- SizeList[Index] = FrameSize;\r
- cursor += SizeRead;\r
- LastBufferSize -= FrameSize + SizeRead;\r
- }\r
- SizeList[Index] = LastBufferSize;\r
- break;\r
- case LACING_FIXED:\r
- for (Index=0; Index<=FrameNum; Index++) {\r
- // get the size of the frame\r
- SizeList[Index] = LastBufferSize / (FrameNum + 1);\r
- }\r
- break;\r
- default: // other lacing not supported\r
- assert(0);\r
- }\r
-\r
- FirstFrameLocation += cursor - EbmlBinary::GetBuffer();\r
-\r
- for (Index=0; Index<=FrameNum; Index++) {\r
- DataBuffer * lacedFrame = new DataBuffer(cursor, SizeList[Index]);\r
- myBuffers.push_back(lacedFrame);\r
- cursor += SizeList[Index];\r
- }\r
- }\r
- SetValueIsSet();\r
- }\r
- else if (ReadFully == SCOPE_PARTIAL_DATA)\r
- {\r
- binary _TempHead[5];\r
- Result = input.read(_TempHead, 5);\r
- binary *cursor = _TempHead;\r
- binary *_tmpBuf;\r
- uint8 BlockHeadSize = 4;\r
-\r
- // update internal values\r
- TrackNumber = *cursor++;\r
- if ((TrackNumber & 0x80) == 0) {\r
- // there is extra data\r
- if ((TrackNumber & 0x40) == 0) {\r
- // We don't support track numbers that large !\r
- return Result;\r
- }\r
- TrackNumber = (TrackNumber & 0x3F) << 8;\r
- TrackNumber += *cursor++;\r
- BlockHeadSize++;\r
- } else {\r
- TrackNumber &= 0x7F;\r
- }\r
-\r
- big_int16 b16;\r
- b16.Eval(cursor);\r
- LocalTimecode = int16(b16);\r
- bLocalTimecodeUsed = true;\r
- cursor += 2;\r
-\r
- if (EbmlId(*this) == EBML_ID(KaxSimpleBlock)) {\r
- bIsKeyframe = (*cursor & 0x80) != 0;\r
- bIsDiscardable = (*cursor & 0x01) != 0;\r
- }\r
- mInvisible = (*cursor & 0x08) >> 3;\r
- mLacing = LacingType((*cursor++ & 0x06) >> 1);\r
- if (cursor == &_TempHead[4])\r
- {\r
- _TempHead[0] = _TempHead[4];\r
- } else {\r
- Result += input.read(_TempHead, 1);\r
- }\r
-\r
- FirstFrameLocation += cursor - _TempHead;\r
-\r
- // put all Frames in the list\r
- if (mLacing != LACING_NONE) {\r
- // read the number of frames in the lace\r
- uint32 LastBufferSize = GetSize() - BlockHeadSize - 1; // 1 for number of frame\r
- uint8 FrameNum = _TempHead[0]; // number of frames in the lace - 1\r
- // read the list of frame sizes\r
- uint8 Index;\r
- int32 FrameSize;\r
- uint32 SizeRead;\r
- uint64 SizeUnknown;\r
-\r
- SizeList.resize(FrameNum + 1);\r
-\r
- switch (mLacing)\r
- {\r
- case LACING_XIPH:\r
- for (Index=0; Index<FrameNum; Index++) {\r
- // get the size of the frame\r
- FrameSize = 0;\r
- do {\r
- Result += input.read(_TempHead, 1);\r
- FrameSize += uint8(_TempHead[0]);\r
- LastBufferSize--;\r
-\r
- FirstFrameLocation++;\r
- } while (_TempHead[0] == 0xFF);\r
-\r
- FirstFrameLocation++;\r
- SizeList[Index] = FrameSize;\r
- LastBufferSize -= FrameSize;\r
- }\r
- SizeList[Index] = LastBufferSize;\r
- break;\r
- case LACING_EBML:\r
- SizeRead = LastBufferSize;\r
- cursor = _tmpBuf = new binary[FrameNum*4]; /// \warning assume the mean size will be coded in less than 4 bytes\r
- Result += input.read(cursor, FrameNum*4);\r
- FrameSize = ReadCodedSizeValue(cursor, SizeRead, SizeUnknown);\r
- SizeList[0] = FrameSize;\r
- cursor += SizeRead;\r
- LastBufferSize -= FrameSize + SizeRead;\r
-\r
- for (Index=1; Index<FrameNum; Index++) {\r
- // get the size of the frame\r
- SizeRead = LastBufferSize;\r
- FrameSize += ReadCodedSizeSignedValue(cursor, SizeRead, SizeUnknown);\r
- SizeList[Index] = FrameSize;\r
- cursor += SizeRead;\r
- LastBufferSize -= FrameSize + SizeRead;\r
- }\r
-\r
- FirstFrameLocation += cursor - _tmpBuf;\r
-\r
- SizeList[Index] = LastBufferSize;\r
- delete [] _tmpBuf;\r
- break;\r
- case LACING_FIXED:\r
- for (Index=0; Index<=FrameNum; Index++) {\r
- // get the size of the frame\r
- SizeList[Index] = LastBufferSize / (FrameNum + 1);\r
- }\r
- break;\r
- default: // other lacing not supported\r
- assert(0);\r
- }\r
- } else {\r
- SizeList.resize(1);\r
- SizeList[0] = GetSize() - BlockHeadSize;\r
- }\r
- SetValueIsSet(false);\r
- Result = GetSize();\r
- } else {\r
- SetValueIsSet(false);\r
- Result = GetSize();\r
- }\r
-\r
- return Result;\r
-}\r
-\r
-bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing)\r
-{\r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- assert(ParentCluster != NULL);\r
- theBlock.SetParent(*ParentCluster);\r
- ParentTrack = &track;\r
- return theBlock.AddFrame(track, timecode, buffer, lacing);\r
-}\r
-\r
-bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, LacingType lacing)\r
-{\r
-// assert(past_timecode < 0);\r
-\r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- assert(ParentCluster != NULL);\r
- theBlock.SetParent(*ParentCluster);\r
- ParentTrack = &track;\r
- bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);\r
-\r
- KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);\r
- thePastRef.SetReferencedBlock(PastBlock);\r
- thePastRef.SetParentBlock(*this);\r
-\r
- return bRes;\r
-}\r
-\r
-bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing)\r
-{\r
-// assert(past_timecode < 0);\r
-\r
-// assert(forw_timecode > 0);\r
- \r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- assert(ParentCluster != NULL);\r
- theBlock.SetParent(*ParentCluster);\r
- ParentTrack = &track;\r
- bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);\r
-\r
- KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);\r
- thePastRef.SetReferencedBlock(PastBlock);\r
- thePastRef.SetParentBlock(*this);\r
-\r
- KaxReferenceBlock & theFutureRef = AddNewChild<KaxReferenceBlock>(*this);\r
- theFutureRef.SetReferencedBlock(ForwBlock);\r
- theFutureRef.SetParentBlock(*this);\r
-\r
- return bRes;\r
-}\r
-\r
-bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock, LacingType lacing)\r
-{\r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- assert(ParentCluster != NULL);\r
- theBlock.SetParent(*ParentCluster);\r
- ParentTrack = &track;\r
- bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);\r
-\r
- if (PastBlock != NULL)\r
- {\r
- KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);\r
- thePastRef.SetReferencedBlock(PastBlock);\r
- thePastRef.SetParentBlock(*this);\r
- }\r
-\r
- if (ForwBlock != NULL)\r
- {\r
- KaxReferenceBlock & theFutureRef = AddNewChild<KaxReferenceBlock>(*this);\r
- theFutureRef.SetReferencedBlock(ForwBlock);\r
- theFutureRef.SetParentBlock(*this);\r
- }\r
-\r
- return bRes;\r
-}\r
-\r
-/*!\r
- \todo we may cache the reference to the timecode block\r
-*/\r
-uint64 KaxBlockGroup::GlobalTimecode() const\r
-{\r
- assert(ParentCluster != NULL); // impossible otherwise\r
- KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));\r
- return MyBlock.GlobalTimecode();\r
-\r
-}\r
-\r
-uint16 KaxBlockGroup::TrackNumber() const\r
-{\r
- KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));\r
- return MyBlock.TrackNum();\r
-}\r
-\r
-uint64 KaxBlockGroup::ClusterPosition() const\r
-{\r
- assert(ParentCluster != NULL); // impossible otherwise\r
- return ParentCluster->GetPosition();\r
-}\r
-\r
-uint64 KaxInternalBlock::ClusterPosition() const\r
-{\r
- assert(ParentCluster != NULL); // impossible otherwise\r
- return ParentCluster->GetPosition();\r
-}\r
-\r
-unsigned int KaxBlockGroup::ReferenceCount() const\r
-{\r
- unsigned int Result = 0;\r
- KaxReferenceBlock * MyBlockAdds = static_cast<KaxReferenceBlock *>(FindFirstElt(EBML_INFO(KaxReferenceBlock)));\r
- if (MyBlockAdds != NULL) {\r
- Result++;\r
- while ((MyBlockAdds = static_cast<KaxReferenceBlock *>(FindNextElt(*MyBlockAdds))) != NULL)\r
- {\r
- Result++;\r
- }\r
- }\r
- return Result;\r
-}\r
-\r
-const KaxReferenceBlock & KaxBlockGroup::Reference(unsigned int Index) const\r
-{\r
- KaxReferenceBlock * MyBlockAdds = static_cast<KaxReferenceBlock *>(FindFirstElt(EBML_INFO(KaxReferenceBlock)));\r
- assert(MyBlockAdds != NULL); // call of a non existing reference\r
- \r
- while (Index != 0) {\r
- MyBlockAdds = static_cast<KaxReferenceBlock *>(FindNextElt(*MyBlockAdds));\r
- assert(MyBlockAdds != NULL);\r
- Index--;\r
- }\r
- return *MyBlockAdds;\r
-}\r
-\r
-void KaxBlockGroup::ReleaseFrames()\r
-{\r
- KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));\r
- MyBlock.ReleaseFrames();\r
-}\r
-\r
-void KaxInternalBlock::ReleaseFrames()\r
-{\r
- // free the allocated Frames\r
- int i;\r
- for (i=myBuffers.size()-1; i>=0; i--) {\r
- if (myBuffers[i] != NULL) {\r
- myBuffers[i]->FreeBuffer(*myBuffers[i]);\r
- delete myBuffers[i];\r
- myBuffers[i] = NULL;\r
- }\r
- }\r
-}\r
-\r
-void KaxBlockGroup::SetBlockDuration(uint64 TimeLength)\r
-{\r
- assert(ParentTrack != NULL);\r
- int64 scale = ParentTrack->GlobalTimecodeScale();\r
- KaxBlockDuration & myDuration = *static_cast<KaxBlockDuration *>(FindFirstElt(EBML_INFO(KaxBlockDuration), true));\r
- *(static_cast<EbmlUInteger *>(&myDuration)) = TimeLength / uint64(scale);\r
-}\r
-\r
-bool KaxBlockGroup::GetBlockDuration(uint64 &TheTimecode) const\r
-{\r
- KaxBlockDuration * myDuration = static_cast<KaxBlockDuration *>(FindElt(EBML_INFO(KaxBlockDuration)));\r
- if (myDuration == NULL) {\r
- return false;\r
- }\r
-\r
- assert(ParentTrack != NULL);\r
- TheTimecode = uint64(*myDuration) * ParentTrack->GlobalTimecodeScale();\r
- return true;\r
-}\r
-\r
-KaxBlockGroup::operator KaxInternalBlock &() {\r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- return theBlock;\r
-}\r
-\r
-void KaxBlockGroup::SetParent(KaxCluster & aParentCluster) {\r
- ParentCluster = &aParentCluster;\r
- KaxBlock & theBlock = GetChild<KaxBlock>(*this);\r
- theBlock.SetParent( aParentCluster );\r
-}\r
-\r
-void KaxInternalBlock::SetParent(KaxCluster & aParentCluster)\r
-{\r
- ParentCluster = &aParentCluster;\r
- if (bLocalTimecodeUsed) {\r
- Timecode = aParentCluster.GetBlockGlobalTimecode(LocalTimecode);\r
- bLocalTimecodeUsed = false;\r
- }\r
-}\r
-\r
-int64 KaxInternalBlock::GetDataPosition(size_t FrameNumber)\r
-{\r
- int64 _Result = -1;\r
-\r
- if (ValueIsSet() && FrameNumber < SizeList.size())\r
- {\r
- _Result = FirstFrameLocation;\r
- \r
- size_t _Idx = 0;\r
- while(FrameNumber--)\r
- {\r
- _Result += SizeList[_Idx++];\r
- }\r
- }\r
-\r
- return _Result;\r
-}\r
-\r
-int64 KaxInternalBlock::GetFrameSize(size_t FrameNumber)\r
-{\r
- int64 _Result = -1;\r
-\r
- if (/*bValueIsSet &&*/ FrameNumber < SizeList.size())\r
- {\r
- _Result = SizeList[FrameNumber];\r
- }\r
-\r
- return _Result;\r
-}\r
-\r
-KaxBlockBlob::operator KaxBlockGroup &()\r
-{\r
- assert(!bUseSimpleBlock);\r
- assert(Block.group);\r
- return *Block.group;\r
-}\r
-\r
-KaxBlockBlob::operator const KaxBlockGroup &() const\r
-{\r
- assert(!bUseSimpleBlock);\r
- assert(Block.group);\r
- return *Block.group;\r
-}\r
-\r
-KaxBlockBlob::operator KaxInternalBlock &()\r
-{\r
- assert(Block.group);\r
-#if MATROSKA_VERSION >= 2\r
- if (bUseSimpleBlock)\r
- return *Block.simpleblock;\r
- else\r
-#endif\r
- return *Block.group;\r
-}\r
-\r
-KaxBlockBlob::operator const KaxInternalBlock &() const\r
-{\r
- assert(Block.group);\r
-#if MATROSKA_VERSION >= 2\r
- if (bUseSimpleBlock)\r
- return *Block.simpleblock;\r
- else\r
-#endif\r
- return *Block.group;\r
-}\r
-\r
-#if MATROSKA_VERSION >= 2\r
-KaxBlockBlob::operator KaxSimpleBlock &()\r
-{\r
- assert(bUseSimpleBlock);\r
- assert(Block.simpleblock);\r
- return *Block.simpleblock;\r
-}\r
-#endif\r
-\r
-bool KaxBlockBlob::AddFrameAuto(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock)\r
-{\r
- bool bResult = false;\r
-#if MATROSKA_VERSION >= 2\r
- if ((SimpleBlockMode == BLOCK_BLOB_ALWAYS_SIMPLE) || (SimpleBlockMode == BLOCK_BLOB_SIMPLE_AUTO && PastBlock == NULL && ForwBlock == NULL)) {\r
- assert(bUseSimpleBlock == true);\r
- if (Block.simpleblock == NULL) {\r
- Block.simpleblock = new KaxSimpleBlock();\r
- Block.simpleblock->SetParent(*ParentCluster);\r
- }\r
-\r
- bResult = Block.simpleblock->AddFrame(track, timecode, buffer, lacing);\r
- if (PastBlock == NULL && ForwBlock == NULL) {\r
- Block.simpleblock->SetKeyframe(true);\r
- Block.simpleblock->SetDiscardable(false);\r
- } else {\r
- Block.simpleblock->SetKeyframe(false);\r
- if ((ForwBlock == NULL || ((const KaxInternalBlock &)*ForwBlock).GlobalTimecode() <= timecode) &&\r
- (PastBlock == NULL || ((const KaxInternalBlock &)*PastBlock).GlobalTimecode() <= timecode))\r
- Block.simpleblock->SetDiscardable(false);\r
- else\r
- Block.simpleblock->SetDiscardable(true);\r
- }\r
- }\r
- else\r
-#endif\r
- {\r
- if (ReplaceSimpleByGroup()) {\r
- bResult = Block.group->AddFrame(track, timecode, buffer, PastBlock, ForwBlock, lacing);\r
- }\r
- }\r
-\r
- return bResult;\r
-}\r
-\r
-void KaxBlockBlob::SetParent(KaxCluster & parent_clust)\r
-{\r
- ParentCluster = &parent_clust;\r
-}\r
-\r
-void KaxBlockBlob::SetBlockDuration(uint64 TimeLength)\r
-{\r
- if (ReplaceSimpleByGroup())\r
- Block.group->SetBlockDuration(TimeLength);\r
-}\r
-\r
-bool KaxBlockBlob::ReplaceSimpleByGroup()\r
-{\r
- if (SimpleBlockMode== BLOCK_BLOB_ALWAYS_SIMPLE)\r
- return false;\r
-\r
- if (!bUseSimpleBlock) {\r
- if (Block.group == NULL) {\r
- Block.group = new KaxBlockGroup();\r
- }\r
- }\r
-#if MATROSKA_VERSION >= 2\r
- else \r
- {\r
-\r
- if (Block.simpleblock != NULL) {\r
- KaxSimpleBlock *old_simpleblock = Block.simpleblock;\r
- Block.group = new KaxBlockGroup();\r
- // _TODO_ : move all the data to the blockgroup\r
- assert(false);\r
- // -> while(frame) AddFrame(myBuffer)\r
- delete old_simpleblock;\r
- } else {\r
- Block.group = new KaxBlockGroup();\r
- }\r
- }\r
-#endif\r
- if (ParentCluster != NULL)\r
- Block.group->SetParent(*ParentCluster);\r
-\r
- bUseSimpleBlock = false;\r
- return true;\r
-}\r
-\r
-void KaxBlockBlob::SetBlockGroup( KaxBlockGroup &BlockRef )\r
-{\r
- assert(!bUseSimpleBlock);\r
- Block.group = &BlockRef;\r
-}\r
-\r
-END_LIBMATROSKA_NAMESPACE\r
+/****************************************************************************
+** libmatroska : parse Matroska files, see http://www.matroska.org/
+**
+** <file/class description>
+**
+** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved.
+**
+** 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: KaxBlock.cpp 1265 2007-01-14 17:20:35Z mosu $
+ \author Steve Lhomme <robux4 @ users.sf.net>
+ \author Julien Coloos <suiryc @ users.sf.net>
+*/
+#include <cassert>
+
+//#include <streams.h>
+
+#include "matroska/KaxBlock.h"
+#include "matroska/KaxContexts.h"
+#include "matroska/KaxBlockData.h"
+#include "matroska/KaxCluster.h"
+#include "matroska/KaxDefines.h"
+
+START_LIBMATROSKA_NAMESPACE
+
+DEFINE_START_SEMANTIC(KaxBlockGroup)
+DEFINE_SEMANTIC_ITEM(true, true, KaxBlock)
+#if MATROSKA_VERSION >= 2
+DEFINE_SEMANTIC_ITEM(false, true, KaxBlockVirtual)
+#endif // MATROSKA_VERSION
+DEFINE_SEMANTIC_ITEM(false, true, KaxBlockDuration)
+DEFINE_SEMANTIC_ITEM(false, true, KaxSlices)
+DEFINE_SEMANTIC_ITEM(true, true, KaxReferencePriority)
+DEFINE_SEMANTIC_ITEM(false, false, KaxReferenceBlock)
+#if MATROSKA_VERSION >= 2
+DEFINE_SEMANTIC_ITEM(false, true, KaxReferenceVirtual)
+DEFINE_SEMANTIC_ITEM(false, true, KaxCodecState)
+#endif // MATROSKA_VERSION
+DEFINE_SEMANTIC_ITEM(false, true, KaxBlockAdditions)
+DEFINE_END_SEMANTIC(KaxBlockGroup)
+
+DEFINE_START_SEMANTIC(KaxBlockAdditions)
+DEFINE_SEMANTIC_ITEM(true, false, KaxBlockMore)
+DEFINE_END_SEMANTIC(KaxBlockAdditions)
+
+DEFINE_START_SEMANTIC(KaxBlockMore)
+DEFINE_SEMANTIC_ITEM(true, true, KaxBlockAddID)
+DEFINE_SEMANTIC_ITEM(true, true, KaxBlockAdditional)
+DEFINE_END_SEMANTIC(KaxBlockMore)
+
+DEFINE_MKX_MASTER_CONS (KaxBlockGroup, 0xA0, 1, KaxCluster, "BlockGroup");
+DEFINE_MKX_BINARY_CONS (KaxBlock, 0xA1, 1, KaxBlockGroup, "Block");
+DEFINE_MKX_UINTEGER (KaxBlockDuration, 0x9B, 1, KaxBlockGroup, "BlockDuration");
+#if MATROSKA_VERSION >= 2
+DEFINE_MKX_BINARY_CONS (KaxSimpleBlock, 0xA3, 1, KaxCluster, "SimpleBlock");
+DEFINE_MKX_BINARY_CONS (KaxBlockVirtual, 0xA2, 1, KaxBlockGroup, "BlockVirtual");
+DEFINE_MKX_BINARY (KaxCodecState, 0xA4, 1, KaxBlockGroup, "CodecState");
+#endif
+DEFINE_MKX_MASTER (KaxBlockAdditions, 0x75A1, 2, KaxBlockGroup, "BlockAdditions");
+DEFINE_MKX_MASTER (KaxBlockMore, 0xA6, 1, KaxBlockAdditions, "BlockMore");
+DEFINE_MKX_UINTEGER_DEF(KaxBlockAddID, 0xEE, 1, KaxBlockMore, "BlockAddID", 1);
+DEFINE_MKX_BINARY (KaxBlockAdditional, 0xA5, 1, KaxBlockMore, "BlockAdditional");
+
+
+DataBuffer * DataBuffer::Clone()
+{
+ binary *ClonedData = (binary *)malloc(mySize * sizeof(binary));
+ assert(ClonedData != NULL);
+ memcpy(ClonedData, myBuffer ,mySize );
+
+ SimpleDataBuffer * result = new SimpleDataBuffer(ClonedData, mySize, 0);
+ result->bValidValue = bValidValue;
+ return result;
+}
+
+SimpleDataBuffer::SimpleDataBuffer(const SimpleDataBuffer & ToClone)
+ :DataBuffer((binary *)malloc(ToClone.mySize * sizeof(binary)), ToClone.mySize, myFreeBuffer)
+{
+ assert(myBuffer != NULL);
+ memcpy(myBuffer, ToClone.myBuffer ,mySize );
+ bValidValue = ToClone.bValidValue;
+}
+
+bool KaxInternalBlock::ValidateSize() const
+{
+ return (GetSize() >= 4); /// for the moment
+}
+
+KaxInternalBlock::~KaxInternalBlock()
+{
+ ReleaseFrames();
+}
+
+KaxInternalBlock::KaxInternalBlock(const KaxInternalBlock & ElementToClone)
+ :EbmlBinary(ElementToClone)
+ ,myBuffers(ElementToClone.myBuffers.size())
+ ,Timecode(ElementToClone.Timecode)
+ ,LocalTimecode(ElementToClone.LocalTimecode)
+ ,bLocalTimecodeUsed(ElementToClone.bLocalTimecodeUsed)
+ ,TrackNumber(ElementToClone.TrackNumber)
+ ,ParentCluster(ElementToClone.ParentCluster) ///< \todo not exactly
+{
+ // add a clone of the list
+ std::vector<DataBuffer *>::const_iterator Itr = ElementToClone.myBuffers.begin();
+ std::vector<DataBuffer *>::iterator myItr = myBuffers.begin();
+ while (Itr != ElementToClone.myBuffers.end())
+ {
+ *myItr = (*Itr)->Clone();
+ Itr++; myItr++;
+ }
+}
+
+
+KaxBlockGroup::~KaxBlockGroup()
+{
+//NOTE("KaxBlockGroup::~KaxBlockGroup");
+}
+
+KaxBlockGroup::KaxBlockGroup()
+ :EbmlMaster(EBML_CLASS_SEMCONTEXT(KaxBlockGroup))
+ ,ParentCluster(NULL)
+ ,ParentTrack(NULL)
+{}
+
+/*!
+ \todo handle flags
+ \todo hardcoded limit of the number of frames in a lace should be a parameter
+ \return true if more frames can be added to this Block
+*/
+bool KaxInternalBlock::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing, bool invisible)
+{
+ SetValueIsSet();
+ if (myBuffers.size() == 0) {
+ // first frame
+ Timecode = timecode;
+ TrackNumber = track.TrackNumber();
+ mInvisible = invisible;
+ mLacing = lacing;
+ }
+ myBuffers.push_back(&buffer);
+
+ // we don't allow more than 8 frames in a Block because the overhead improvement is minimal
+ if (myBuffers.size() >= 8 || lacing == LACING_NONE)
+ return false;
+
+ if (lacing == LACING_XIPH)
+ // decide wether a new frame can be added or not
+ // a frame in a lace is not efficient when the place necessary to code it in a lace is bigger
+ // than the size of a simple Block. That means more than 6 bytes (4 in struct + 2 for EBML) to code the size
+ return (buffer.Size() < 6*0xFF);
+ else
+ return true;
+}
+
+/*!
+ \return Returns the lacing type that produces the smallest footprint.
+*/
+LacingType KaxInternalBlock::GetBestLacingType() const {
+ int XiphLacingSize, EbmlLacingSize, i;
+ bool SameSize = true;
+
+ if (myBuffers.size() <= 1)
+ return LACING_NONE;
+
+ XiphLacingSize = 1; // Number of laces is stored in 1 byte.
+ EbmlLacingSize = 1;
+ for (i = 0; i < (int)myBuffers.size() - 1; i++) {
+ if (myBuffers[i]->Size() != myBuffers[i + 1]->Size())
+ SameSize = false;
+ XiphLacingSize += myBuffers[i]->Size() / 255 + 1;
+ }
+ EbmlLacingSize += CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize());
+ for (i = 1; i < (int)myBuffers.size() - 1; i++)
+ EbmlLacingSize += CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i - 1]->Size()), 0);
+ if (SameSize)
+ return LACING_FIXED;
+ else if (XiphLacingSize < EbmlLacingSize)
+ return LACING_XIPH;
+ else
+ return LACING_EBML;
+}
+
+filepos_t KaxInternalBlock::UpdateSize(bool bSaveDefault, bool bForceRender)
+{
+ LacingType LacingHere;
+ assert(EbmlBinary::GetBuffer() == NULL); // Data is not used for KaxInternalBlock
+ assert(TrackNumber < 0x4000); // no more allowed for the moment
+ unsigned int i;
+
+ // compute the final size of the data
+ switch (myBuffers.size()) {
+ case 0:
+ SetSize_(0);
+ break;
+ case 1:
+ SetSize_(4 + myBuffers[0]->Size());
+ break;
+ default:
+ SetSize_(4 + 1); // 1 for the lacing head
+ if (mLacing == LACING_AUTO)
+ LacingHere = GetBestLacingType();
+ else
+ LacingHere = mLacing;
+ switch (LacingHere)
+ {
+ case LACING_XIPH:
+ for (i=0; i<myBuffers.size()-1; i++) {
+ SetSize_(GetSize() + myBuffers[i]->Size() + (myBuffers[i]->Size() / 0xFF + 1));
+ }
+ break;
+ case LACING_EBML:
+ SetSize_(GetSize() + myBuffers[0]->Size() + CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize()));
+ for (i=1; i<myBuffers.size()-1; i++) {
+ SetSize_(GetSize() + myBuffers[i]->Size() + CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size()), 0));
+ }
+ break;
+ case LACING_FIXED:
+ for (i=0; i<myBuffers.size()-1; i++) {
+ SetSize_(GetSize() + myBuffers[i]->Size());
+ }
+ break;
+ default:
+ assert(0);
+ }
+ // Size of the last frame (not in lace)
+ SetSize_(GetSize() + myBuffers[i]->Size());
+ break;
+ }
+
+ if (TrackNumber >= 0x80)
+ SetSize_(GetSize() + 1); // the size will be coded with one more octet
+
+ return GetSize();
+}
+
+#if MATROSKA_VERSION >= 2
+KaxBlockVirtual::KaxBlockVirtual(const KaxBlockVirtual & ElementToClone)
+ :EbmlBinary(ElementToClone)
+ ,Timecode(ElementToClone.Timecode)
+ ,TrackNumber(ElementToClone.TrackNumber)
+ ,ParentCluster(ElementToClone.ParentCluster) ///< \todo not exactly
+{
+ SetBuffer(DataBlock,sizeof(DataBlock));
+ SetValueIsSet(false);
+}
+
+KaxBlockVirtual::KaxBlockVirtual()
+:ParentCluster(NULL)
+{
+ SetBuffer(DataBlock,sizeof(DataBlock));
+ SetValueIsSet(false);
+}
+
+KaxBlockVirtual::~KaxBlockVirtual()
+{
+ if(GetBuffer() == DataBlock)
+ SetBuffer( NULL, 0 );
+}
+
+filepos_t KaxBlockVirtual::UpdateSize(bool bSaveDefault, bool bForceRender)
+{
+ assert(TrackNumber < 0x4000);
+ binary *cursor = EbmlBinary::GetBuffer();
+ // fill data
+ if (TrackNumber < 0x80) {
+ *cursor++ = TrackNumber | 0x80; // set the first bit to 1
+ } else {
+ *cursor++ = (TrackNumber >> 8) | 0x40; // set the second bit to 1
+ *cursor++ = TrackNumber & 0xFF;
+ }
+
+ assert(ParentCluster != NULL);
+ int16 ActualTimecode = ParentCluster->GetBlockLocalTimecode(Timecode);
+ big_int16 b16(ActualTimecode);
+ b16.Fill(cursor);
+ cursor += 2;
+
+ *cursor++ = 0; // flags
+
+ return GetSize();
+}
+#endif // MATROSKA_VERSION
+
+/*!
+ \todo more optimisation is possible (render the Block head and don't copy the buffer in memory, care should be taken with the allocation of Data)
+ \todo the actual timecode to write should be retrieved from the Cluster from here
+*/
+filepos_t KaxInternalBlock::RenderData(IOCallback & output, bool bForceRender, bool bSaveDefault)
+{
+ if (myBuffers.size() == 0) {
+ return 0;
+ } else {
+ assert(TrackNumber < 0x4000);
+ binary BlockHead[5], *cursor = BlockHead;
+ unsigned int i;
+
+ if (myBuffers.size() == 1) {
+ SetSize_(4);
+ mLacing = LACING_NONE;
+ } else {
+ if (mLacing == LACING_NONE)
+ mLacing = LACING_EBML; // supposedly the best of all
+ SetSize_(4 + 1); // 1 for the lacing head (number of laced elements)
+ }
+ if (TrackNumber > 0x80)
+ SetSize_(GetSize() + 1);
+
+ // write Block Head
+ if (TrackNumber < 0x80) {
+ *cursor++ = TrackNumber | 0x80; // set the first bit to 1
+ } else {
+ *cursor++ = (TrackNumber >> 8) | 0x40; // set the second bit to 1
+ *cursor++ = TrackNumber & 0xFF;
+ }
+
+ assert(ParentCluster != NULL);
+ int16 ActualTimecode = ParentCluster->GetBlockLocalTimecode(Timecode);
+ big_int16 b16(ActualTimecode);
+ b16.Fill(cursor);
+ cursor += 2;
+
+ *cursor = 0; // flags
+
+ if (mLacing == LACING_AUTO) {
+ mLacing = GetBestLacingType();
+ }
+
+ // invisible flag
+ if (mInvisible)
+ *cursor = 0x08;
+
+ if (bIsSimple) {
+ if (bIsKeyframe)
+ *cursor |= 0x80;
+ if (bIsDiscardable)
+ *cursor |= 0x01;
+ }
+
+ // lacing flag
+ switch (mLacing)
+ {
+ case LACING_XIPH:
+ *cursor++ |= 0x02;
+ break;
+ case LACING_EBML:
+ *cursor++ |= 0x06;
+ break;
+ case LACING_FIXED:
+ *cursor++ |= 0x04;
+ break;
+ case LACING_NONE:
+ break;
+ default:
+ assert(0);
+ }
+
+ output.writeFully(BlockHead, 4 + ((TrackNumber > 0x80) ? 1 : 0));
+
+ binary tmpValue;
+ switch (mLacing)
+ {
+ case LACING_XIPH:
+ // number of laces
+ tmpValue = myBuffers.size()-1;
+ output.writeFully(&tmpValue, 1);
+
+ // set the size of each member in the lace
+ for (i=0; i<myBuffers.size()-1; i++) {
+ tmpValue = 0xFF;
+ uint16 tmpSize = myBuffers[i]->Size();
+ while (tmpSize >= 0xFF) {
+ output.writeFully(&tmpValue, 1);
+ SetSize_(GetSize() + 1);
+ tmpSize -= 0xFF;
+ }
+ tmpValue = binary(tmpSize);
+ output.writeFully(&tmpValue, 1);
+ SetSize_(GetSize() + 1);
+ }
+ break;
+ case LACING_EBML:
+ // number of laces
+ tmpValue = myBuffers.size()-1;
+ output.writeFully(&tmpValue, 1);
+
+ {
+ int64 _Size;
+ int _CodedSize;
+ binary _FinalHead[8]; // 64 bits max coded size
+
+ _Size = myBuffers[0]->Size();
+
+ _CodedSize = CodedSizeLength(_Size, 0, IsFiniteSize());
+
+ // first size in the lace is not a signed
+ CodedValueLength(_Size, _CodedSize, _FinalHead);
+ output.writeFully(_FinalHead, _CodedSize);
+ SetSize_(GetSize() + _CodedSize);
+
+ // set the size of each member in the lace
+ for (i=1; i<myBuffers.size()-1; i++) {
+ _Size = int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size());
+ _CodedSize = CodedSizeLengthSigned(_Size, 0);
+ CodedValueLengthSigned(_Size, _CodedSize, _FinalHead);
+ output.writeFully(_FinalHead, _CodedSize);
+ SetSize_(GetSize() + _CodedSize);
+ }
+ }
+ break;
+ case LACING_FIXED:
+ // number of laces
+ tmpValue = myBuffers.size()-1;
+ output.writeFully(&tmpValue, 1);
+ break;
+ case LACING_NONE:
+ break;
+ default:
+ assert(0);
+ }
+
+ // put the data of each frame
+ for (i=0; i<myBuffers.size(); i++) {
+ output.writeFully(myBuffers[i]->Buffer(), myBuffers[i]->Size());
+ SetSize_(GetSize() + myBuffers[i]->Size());
+ }
+ }
+
+ return GetSize();
+}
+
+uint64 KaxInternalBlock::ReadInternalHead(IOCallback & input)
+{
+ binary Buffer[5], *cursor = Buffer;
+ uint64 Result = input.read(cursor, 4);
+ if (Result != 4)
+ return Result;
+
+ // update internal values
+ TrackNumber = *cursor++;
+ if ((TrackNumber & 0x80) == 0) {
+ // there is extra data
+ if ((TrackNumber & 0x40) == 0) {
+ // We don't support track numbers that large !
+ return Result;
+ }
+ Result += input.read(&Buffer[4], 1);
+ TrackNumber = (TrackNumber & 0x3F) << 8;
+ TrackNumber += *cursor++;
+ } else {
+ TrackNumber &= 0x7F;
+ }
+
+
+ big_int16 b16;
+ b16.Eval(cursor);
+ assert(ParentCluster != NULL);
+ Timecode = ParentCluster->GetBlockGlobalTimecode(int16(b16));
+ bLocalTimecodeUsed = false;
+ cursor += 2;
+
+ return Result;
+}
+
+/*!
+ \todo better zero copy handling
+*/
+filepos_t KaxInternalBlock::ReadData(IOCallback & input, ScopeMode ReadFully)
+{
+ filepos_t Result;
+
+ FirstFrameLocation = input.getFilePointer(); // will be updated accordingly below
+
+ if (ReadFully == SCOPE_ALL_DATA)
+ {
+ Result = EbmlBinary::ReadData(input, ReadFully);
+ binary *cursor = EbmlBinary::GetBuffer();
+ uint8 BlockHeadSize = 4;
+
+ // update internal values
+ TrackNumber = *cursor++;
+ if ((TrackNumber & 0x80) == 0) {
+ // there is extra data
+ if ((TrackNumber & 0x40) == 0) {
+ // We don't support track numbers that large !
+ return Result;
+ }
+ TrackNumber = (TrackNumber & 0x3F) << 8;
+ TrackNumber += *cursor++;
+ BlockHeadSize++;
+ } else {
+ TrackNumber &= 0x7F;
+ }
+
+ big_int16 b16;
+ b16.Eval(cursor);
+ LocalTimecode = int16(b16);
+ bLocalTimecodeUsed = true;
+ cursor += 2;
+
+ if (EbmlId(*this) == EBML_ID(KaxSimpleBlock)) {
+ bIsKeyframe = (*cursor & 0x80) != 0;
+ bIsDiscardable = (*cursor & 0x01) != 0;
+ }
+ mInvisible = (*cursor & 0x08) >> 3;
+ mLacing = LacingType((*cursor++ & 0x06) >> 1);
+
+ // put all Frames in the list
+ if (mLacing == LACING_NONE) {
+ FirstFrameLocation += cursor - EbmlBinary::GetBuffer();
+ DataBuffer * soloFrame = new DataBuffer(cursor, GetSize() - BlockHeadSize);
+ myBuffers.push_back(soloFrame);
+ SizeList.resize(1);
+ SizeList[0] = GetSize() - BlockHeadSize;
+ } else {
+ // read the number of frames in the lace
+ uint32 LastBufferSize = GetSize() - BlockHeadSize - 1; // 1 for number of frame
+ uint8 FrameNum = *cursor++; // number of frames in the lace - 1
+ // read the list of frame sizes
+ uint8 Index;
+ int32 FrameSize;
+ uint32 SizeRead;
+ uint64 SizeUnknown;
+
+ SizeList.resize(FrameNum + 1);
+
+ switch (mLacing)
+ {
+ case LACING_XIPH:
+ for (Index=0; Index<FrameNum; Index++) {
+ // get the size of the frame
+ FrameSize = 0;
+ do {
+ FrameSize += uint8(*cursor);
+ LastBufferSize--;
+ } while (*cursor++ == 0xFF);
+ SizeList[Index] = FrameSize;
+ LastBufferSize -= FrameSize;
+ }
+ SizeList[Index] = LastBufferSize;
+ break;
+ case LACING_EBML:
+ SizeRead = LastBufferSize;
+ FrameSize = ReadCodedSizeValue(cursor, SizeRead, SizeUnknown);
+ SizeList[0] = FrameSize;
+ cursor += SizeRead;
+ LastBufferSize -= FrameSize + SizeRead;
+
+ for (Index=1; Index<FrameNum; Index++) {
+ // get the size of the frame
+ SizeRead = LastBufferSize;
+ FrameSize += ReadCodedSizeSignedValue(cursor, SizeRead, SizeUnknown);
+ SizeList[Index] = FrameSize;
+ cursor += SizeRead;
+ LastBufferSize -= FrameSize + SizeRead;
+ }
+ SizeList[Index] = LastBufferSize;
+ break;
+ case LACING_FIXED:
+ for (Index=0; Index<=FrameNum; Index++) {
+ // get the size of the frame
+ SizeList[Index] = LastBufferSize / (FrameNum + 1);
+ }
+ break;
+ default: // other lacing not supported
+ assert(0);
+ }
+
+ FirstFrameLocation += cursor - EbmlBinary::GetBuffer();
+
+ for (Index=0; Index<=FrameNum; Index++) {
+ DataBuffer * lacedFrame = new DataBuffer(cursor, SizeList[Index]);
+ myBuffers.push_back(lacedFrame);
+ cursor += SizeList[Index];
+ }
+ }
+ SetValueIsSet();
+ }
+ else if (ReadFully == SCOPE_PARTIAL_DATA)
+ {
+ binary _TempHead[5];
+ Result = input.read(_TempHead, 5);
+ binary *cursor = _TempHead;
+ binary *_tmpBuf;
+ uint8 BlockHeadSize = 4;
+
+ // update internal values
+ TrackNumber = *cursor++;
+ if ((TrackNumber & 0x80) == 0) {
+ // there is extra data
+ if ((TrackNumber & 0x40) == 0) {
+ // We don't support track numbers that large !
+ return Result;
+ }
+ TrackNumber = (TrackNumber & 0x3F) << 8;
+ TrackNumber += *cursor++;
+ BlockHeadSize++;
+ } else {
+ TrackNumber &= 0x7F;
+ }
+
+ big_int16 b16;
+ b16.Eval(cursor);
+ LocalTimecode = int16(b16);
+ bLocalTimecodeUsed = true;
+ cursor += 2;
+
+ if (EbmlId(*this) == EBML_ID(KaxSimpleBlock)) {
+ bIsKeyframe = (*cursor & 0x80) != 0;
+ bIsDiscardable = (*cursor & 0x01) != 0;
+ }
+ mInvisible = (*cursor & 0x08) >> 3;
+ mLacing = LacingType((*cursor++ & 0x06) >> 1);
+ if (cursor == &_TempHead[4])
+ {
+ _TempHead[0] = _TempHead[4];
+ } else {
+ Result += input.read(_TempHead, 1);
+ }
+
+ FirstFrameLocation += cursor - _TempHead;
+
+ // put all Frames in the list
+ if (mLacing != LACING_NONE) {
+ // read the number of frames in the lace
+ uint32 LastBufferSize = GetSize() - BlockHeadSize - 1; // 1 for number of frame
+ uint8 FrameNum = _TempHead[0]; // number of frames in the lace - 1
+ // read the list of frame sizes
+ uint8 Index;
+ int32 FrameSize;
+ uint32 SizeRead;
+ uint64 SizeUnknown;
+
+ SizeList.resize(FrameNum + 1);
+
+ switch (mLacing)
+ {
+ case LACING_XIPH:
+ for (Index=0; Index<FrameNum; Index++) {
+ // get the size of the frame
+ FrameSize = 0;
+ do {
+ Result += input.read(_TempHead, 1);
+ FrameSize += uint8(_TempHead[0]);
+ LastBufferSize--;
+
+ FirstFrameLocation++;
+ } while (_TempHead[0] == 0xFF);
+
+ FirstFrameLocation++;
+ SizeList[Index] = FrameSize;
+ LastBufferSize -= FrameSize;
+ }
+ SizeList[Index] = LastBufferSize;
+ break;
+ case LACING_EBML:
+ SizeRead = LastBufferSize;
+ cursor = _tmpBuf = new binary[FrameNum*4]; /// \warning assume the mean size will be coded in less than 4 bytes
+ Result += input.read(cursor, FrameNum*4);
+ FrameSize = ReadCodedSizeValue(cursor, SizeRead, SizeUnknown);
+ SizeList[0] = FrameSize;
+ cursor += SizeRead;
+ LastBufferSize -= FrameSize + SizeRead;
+
+ for (Index=1; Index<FrameNum; Index++) {
+ // get the size of the frame
+ SizeRead = LastBufferSize;
+ FrameSize += ReadCodedSizeSignedValue(cursor, SizeRead, SizeUnknown);
+ SizeList[Index] = FrameSize;
+ cursor += SizeRead;
+ LastBufferSize -= FrameSize + SizeRead;
+ }
+
+ FirstFrameLocation += cursor - _tmpBuf;
+
+ SizeList[Index] = LastBufferSize;
+ delete [] _tmpBuf;
+ break;
+ case LACING_FIXED:
+ for (Index=0; Index<=FrameNum; Index++) {
+ // get the size of the frame
+ SizeList[Index] = LastBufferSize / (FrameNum + 1);
+ }
+ break;
+ default: // other lacing not supported
+ assert(0);
+ }
+ } else {
+ SizeList.resize(1);
+ SizeList[0] = GetSize() - BlockHeadSize;
+ }
+ SetValueIsSet(false);
+ Result = GetSize();
+ } else {
+ SetValueIsSet(false);
+ Result = GetSize();
+ }
+
+ return Result;
+}
+
+bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing)
+{
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ assert(ParentCluster != NULL);
+ theBlock.SetParent(*ParentCluster);
+ ParentTrack = &track;
+ return theBlock.AddFrame(track, timecode, buffer, lacing);
+}
+
+bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, LacingType lacing)
+{
+// assert(past_timecode < 0);
+
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ assert(ParentCluster != NULL);
+ theBlock.SetParent(*ParentCluster);
+ ParentTrack = &track;
+ bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);
+
+ KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);
+ thePastRef.SetReferencedBlock(PastBlock);
+ thePastRef.SetParentBlock(*this);
+
+ return bRes;
+}
+
+bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing)
+{
+// assert(past_timecode < 0);
+
+// assert(forw_timecode > 0);
+
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ assert(ParentCluster != NULL);
+ theBlock.SetParent(*ParentCluster);
+ ParentTrack = &track;
+ bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);
+
+ KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);
+ thePastRef.SetReferencedBlock(PastBlock);
+ thePastRef.SetParentBlock(*this);
+
+ KaxReferenceBlock & theFutureRef = AddNewChild<KaxReferenceBlock>(*this);
+ theFutureRef.SetReferencedBlock(ForwBlock);
+ theFutureRef.SetParentBlock(*this);
+
+ return bRes;
+}
+
+bool KaxBlockGroup::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock, LacingType lacing)
+{
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ assert(ParentCluster != NULL);
+ theBlock.SetParent(*ParentCluster);
+ ParentTrack = &track;
+ bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing);
+
+ if (PastBlock != NULL)
+ {
+ KaxReferenceBlock & thePastRef = GetChild<KaxReferenceBlock>(*this);
+ thePastRef.SetReferencedBlock(PastBlock);
+ thePastRef.SetParentBlock(*this);
+ }
+
+ if (ForwBlock != NULL)
+ {
+ KaxReferenceBlock & theFutureRef = AddNewChild<KaxReferenceBlock>(*this);
+ theFutureRef.SetReferencedBlock(ForwBlock);
+ theFutureRef.SetParentBlock(*this);
+ }
+
+ return bRes;
+}
+
+/*!
+ \todo we may cache the reference to the timecode block
+*/
+uint64 KaxBlockGroup::GlobalTimecode() const
+{
+ assert(ParentCluster != NULL); // impossible otherwise
+ KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));
+ return MyBlock.GlobalTimecode();
+
+}
+
+uint16 KaxBlockGroup::TrackNumber() const
+{
+ KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));
+ return MyBlock.TrackNum();
+}
+
+uint64 KaxBlockGroup::ClusterPosition() const
+{
+ assert(ParentCluster != NULL); // impossible otherwise
+ return ParentCluster->GetPosition();
+}
+
+uint64 KaxInternalBlock::ClusterPosition() const
+{
+ assert(ParentCluster != NULL); // impossible otherwise
+ return ParentCluster->GetPosition();
+}
+
+unsigned int KaxBlockGroup::ReferenceCount() const
+{
+ unsigned int Result = 0;
+ KaxReferenceBlock * MyBlockAdds = static_cast<KaxReferenceBlock *>(FindFirstElt(EBML_INFO(KaxReferenceBlock)));
+ if (MyBlockAdds != NULL) {
+ Result++;
+ while ((MyBlockAdds = static_cast<KaxReferenceBlock *>(FindNextElt(*MyBlockAdds))) != NULL)
+ {
+ Result++;
+ }
+ }
+ return Result;
+}
+
+const KaxReferenceBlock & KaxBlockGroup::Reference(unsigned int Index) const
+{
+ KaxReferenceBlock * MyBlockAdds = static_cast<KaxReferenceBlock *>(FindFirstElt(EBML_INFO(KaxReferenceBlock)));
+ assert(MyBlockAdds != NULL); // call of a non existing reference
+
+ while (Index != 0) {
+ MyBlockAdds = static_cast<KaxReferenceBlock *>(FindNextElt(*MyBlockAdds));
+ assert(MyBlockAdds != NULL);
+ Index--;
+ }
+ return *MyBlockAdds;
+}
+
+void KaxBlockGroup::ReleaseFrames()
+{
+ KaxInternalBlock & MyBlock = *static_cast<KaxBlock *>(this->FindElt(EBML_INFO(KaxBlock)));
+ MyBlock.ReleaseFrames();
+}
+
+void KaxInternalBlock::ReleaseFrames()
+{
+ // free the allocated Frames
+ int i;
+ for (i=myBuffers.size()-1; i>=0; i--) {
+ if (myBuffers[i] != NULL) {
+ myBuffers[i]->FreeBuffer(*myBuffers[i]);
+ delete myBuffers[i];
+ myBuffers[i] = NULL;
+ }
+ }
+}
+
+void KaxBlockGroup::SetBlockDuration(uint64 TimeLength)
+{
+ assert(ParentTrack != NULL);
+ int64 scale = ParentTrack->GlobalTimecodeScale();
+ KaxBlockDuration & myDuration = *static_cast<KaxBlockDuration *>(FindFirstElt(EBML_INFO(KaxBlockDuration), true));
+ *(static_cast<EbmlUInteger *>(&myDuration)) = TimeLength / uint64(scale);
+}
+
+bool KaxBlockGroup::GetBlockDuration(uint64 &TheTimecode) const
+{
+ KaxBlockDuration * myDuration = static_cast<KaxBlockDuration *>(FindElt(EBML_INFO(KaxBlockDuration)));
+ if (myDuration == NULL) {
+ return false;
+ }
+
+ assert(ParentTrack != NULL);
+ TheTimecode = uint64(*myDuration) * ParentTrack->GlobalTimecodeScale();
+ return true;
+}
+
+KaxBlockGroup::operator KaxInternalBlock &() {
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ return theBlock;
+}
+
+void KaxBlockGroup::SetParent(KaxCluster & aParentCluster) {
+ ParentCluster = &aParentCluster;
+ KaxBlock & theBlock = GetChild<KaxBlock>(*this);
+ theBlock.SetParent( aParentCluster );
+}
+
+void KaxInternalBlock::SetParent(KaxCluster & aParentCluster)
+{
+ ParentCluster = &aParentCluster;
+ if (bLocalTimecodeUsed) {
+ Timecode = aParentCluster.GetBlockGlobalTimecode(LocalTimecode);
+ bLocalTimecodeUsed = false;
+ }
+}
+
+int64 KaxInternalBlock::GetDataPosition(size_t FrameNumber)
+{
+ int64 _Result = -1;
+
+ if (ValueIsSet() && FrameNumber < SizeList.size())
+ {
+ _Result = FirstFrameLocation;
+
+ size_t _Idx = 0;
+ while(FrameNumber--)
+ {
+ _Result += SizeList[_Idx++];
+ }
+ }
+
+ return _Result;
+}
+
+int64 KaxInternalBlock::GetFrameSize(size_t FrameNumber)
+{
+ int64 _Result = -1;
+
+ if (/*bValueIsSet &&*/ FrameNumber < SizeList.size())
+ {
+ _Result = SizeList[FrameNumber];
+ }
+
+ return _Result;
+}
+
+KaxBlockBlob::operator KaxBlockGroup &()
+{
+ assert(!bUseSimpleBlock);
+ assert(Block.group);
+ return *Block.group;
+}
+
+KaxBlockBlob::operator const KaxBlockGroup &() const
+{
+ assert(!bUseSimpleBlock);
+ assert(Block.group);
+ return *Block.group;
+}
+
+KaxBlockBlob::operator KaxInternalBlock &()
+{
+ assert(Block.group);
+#if MATROSKA_VERSION >= 2
+ if (bUseSimpleBlock)
+ return *Block.simpleblock;
+ else
+#endif
+ return *Block.group;
+}
+
+KaxBlockBlob::operator const KaxInternalBlock &() const
+{
+ assert(Block.group);
+#if MATROSKA_VERSION >= 2
+ if (bUseSimpleBlock)
+ return *Block.simpleblock;
+ else
+#endif
+ return *Block.group;
+}
+
+#if MATROSKA_VERSION >= 2
+KaxBlockBlob::operator KaxSimpleBlock &()
+{
+ assert(bUseSimpleBlock);
+ assert(Block.simpleblock);
+ return *Block.simpleblock;
+}
+#endif
+
+bool KaxBlockBlob::AddFrameAuto(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock)
+{
+ bool bResult = false;
+#if MATROSKA_VERSION >= 2
+ if ((SimpleBlockMode == BLOCK_BLOB_ALWAYS_SIMPLE) || (SimpleBlockMode == BLOCK_BLOB_SIMPLE_AUTO && PastBlock == NULL && ForwBlock == NULL)) {
+ assert(bUseSimpleBlock == true);
+ if (Block.simpleblock == NULL) {
+ Block.simpleblock = new KaxSimpleBlock();
+ Block.simpleblock->SetParent(*ParentCluster);
+ }
+
+ bResult = Block.simpleblock->AddFrame(track, timecode, buffer, lacing);
+ if (PastBlock == NULL && ForwBlock == NULL) {
+ Block.simpleblock->SetKeyframe(true);
+ Block.simpleblock->SetDiscardable(false);
+ } else {
+ Block.simpleblock->SetKeyframe(false);
+ if ((ForwBlock == NULL || ((const KaxInternalBlock &)*ForwBlock).GlobalTimecode() <= timecode) &&
+ (PastBlock == NULL || ((const KaxInternalBlock &)*PastBlock).GlobalTimecode() <= timecode))
+ Block.simpleblock->SetDiscardable(false);
+ else
+ Block.simpleblock->SetDiscardable(true);
+ }
+ }
+ else
+#endif
+ {
+ if (ReplaceSimpleByGroup()) {
+ bResult = Block.group->AddFrame(track, timecode, buffer, PastBlock, ForwBlock, lacing);
+ }
+ }
+
+ return bResult;
+}
+
+void KaxBlockBlob::SetParent(KaxCluster & parent_clust)
+{
+ ParentCluster = &parent_clust;
+}
+
+void KaxBlockBlob::SetBlockDuration(uint64 TimeLength)
+{
+ if (ReplaceSimpleByGroup())
+ Block.group->SetBlockDuration(TimeLength);
+}
+
+bool KaxBlockBlob::ReplaceSimpleByGroup()
+{
+ if (SimpleBlockMode== BLOCK_BLOB_ALWAYS_SIMPLE)
+ return false;
+
+ if (!bUseSimpleBlock) {
+ if (Block.group == NULL) {
+ Block.group = new KaxBlockGroup();
+ }
+ }
+#if MATROSKA_VERSION >= 2
+ else
+ {
+
+ if (Block.simpleblock != NULL) {
+ KaxSimpleBlock *old_simpleblock = Block.simpleblock;
+ Block.group = new KaxBlockGroup();
+ // _TODO_ : move all the data to the blockgroup
+ assert(false);
+ // -> while(frame) AddFrame(myBuffer)
+ delete old_simpleblock;
+ } else {
+ Block.group = new KaxBlockGroup();
+ }
+ }
+#endif
+ if (ParentCluster != NULL)
+ Block.group->SetParent(*ParentCluster);
+
+ bUseSimpleBlock = false;
+ return true;
+}
+
+void KaxBlockBlob::SetBlockGroup( KaxBlockGroup &BlockRef )
+{
+ assert(!bUseSimpleBlock);
+ Block.group = &BlockRef;
+}
+
+END_LIBMATROSKA_NAMESPACE
-/****************************************************************************\r
-** libmatroska : parse Matroska files, see http://www.matroska.org/\r
-**\r
-** <file/class description>\r
-**\r
-** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved.\r
-**\r
-** This library is free software; you can redistribute it and/or\r
-** modify it under the terms of the GNU Lesser General Public\r
-** License as published by the Free Software Foundation; either\r
-** version 2.1 of the License, or (at your option) any later version.\r
-** \r
-** This library is distributed in the hope that it will be useful,\r
-** but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
-** Lesser General Public License for more details.\r
-** \r
-** You should have received a copy of the GNU Lesser General Public\r
-** License along with this library; if not, write to the Free Software\r
-** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
-**\r
-** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.**\r
-** Contact license@matroska.org if any conditions of this licensing are\r
-** not clear to you.\r
-**\r
-**********************************************************************/\r
-\r
-/*!\r
- \file\r
- \version \$Id: KaxCluster.cpp 1228 2005-10-14 19:36:51Z robux4 $\r
- \author Steve Lhomme <robux4 @ users.sf.net>\r
-*/\r
-#include "matroska/KaxCluster.h"\r
-#include "matroska/KaxClusterData.h"\r
-#include "matroska/KaxBlock.h"\r
-#include "matroska/KaxContexts.h"\r
-#include "matroska/KaxSegment.h"\r
-#include "matroska/KaxDefines.h"\r
-\r
-// sub elements\r
-START_LIBMATROSKA_NAMESPACE\r
-\r
-DEFINE_START_SEMANTIC(KaxCluster)\r
-DEFINE_SEMANTIC_ITEM(true, true, KaxClusterTimecode)\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxClusterSilentTracks)\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxClusterPrevSize)\r
-DEFINE_SEMANTIC_ITEM(false, false, KaxBlockGroup)\r
-#if MATROSKA_VERSION == 2\r
-DEFINE_SEMANTIC_ITEM(false, false, KaxSimpleBlock)\r
-#endif\r
-DEFINE_SEMANTIC_ITEM(false, true, KaxClusterPosition)\r
-DEFINE_END_SEMANTIC(KaxCluster)\r
-\r
-DEFINE_MKX_MASTER_CONS(KaxCluster, 0x1F43B675, 4, KaxSegment, "Cluster");\r
-\r
-KaxCluster::KaxCluster()\r
- :EbmlMaster(Context_KaxCluster)\r
- ,currentNewBlock(NULL)\r
- ,ParentSegment(NULL)\r
- ,bFirstFrameInside(false)\r
- ,bPreviousTimecodeIsSet(false)\r
- ,bTimecodeScaleIsSet(false)\r
- ,bSilentTracksUsed(false)\r
-{}\r
-\r
-KaxCluster::KaxCluster(const KaxCluster & ElementToClone) \r
- :EbmlMaster(ElementToClone)\r
- ,bSilentTracksUsed(ElementToClone.bSilentTracksUsed)\r
-{\r
- // update the parent of each children\r
- EBML_MASTER_ITERATOR Itr = begin();\r
- while (Itr != end())\r
- {\r
- if (EbmlId(**Itr) == EBML_ID(KaxBlockGroup)) {\r
- static_cast<KaxBlockGroup *>(*Itr)->SetParent(*this);\r
- } else if (EbmlId(**Itr) == EBML_ID(KaxBlock)) {\r
- static_cast<KaxBlock *>(*Itr)->SetParent(*this);\r
-#if MATROSKA_VERSION >= 2\r
- } else if (EbmlId(**Itr) == EBML_ID(KaxBlockVirtual)) {\r
- static_cast<KaxBlockVirtual *>(*Itr)->SetParent(*this);\r
-#endif // MATROSKA_VERSION\r
- }\r
- ++Itr;\r
- }\r
-}\r
-\r
-bool KaxCluster::AddBlockBlob(KaxBlockBlob * NewBlob)\r
-{\r
- Blobs.push_back(NewBlob);\r
- return true;\r
-}\r
-\r
-bool KaxCluster::AddFrameInternal(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup * PastBlock, const KaxBlockGroup * ForwBlock, LacingType lacing)\r
-{\r
- if (!bFirstFrameInside) {\r
- bFirstFrameInside = true;\r
- MinTimecode = MaxTimecode = timecode;\r
- } else {\r
- if (timecode < MinTimecode)\r
- MinTimecode = timecode;\r
- if (timecode > MaxTimecode)\r
- MaxTimecode = timecode;\r
- }\r
-\r
- MyNewBlock = NULL;\r
-\r
- if (lacing == LACING_NONE || !track.LacingEnabled()) {\r
- currentNewBlock = NULL;\r
- }\r
-\r
- // force creation of a new block\r
- if (currentNewBlock == NULL || uint32(track.TrackNumber()) != uint32(currentNewBlock->TrackNumber()) || PastBlock != NULL || ForwBlock != NULL) {\r
- KaxBlockGroup & aNewBlock = GetNewBlock();\r
- MyNewBlock = currentNewBlock = &aNewBlock;\r
- currentNewBlock = &aNewBlock;\r
- }\r
-\r
- if (PastBlock != NULL) {\r
- if (ForwBlock != NULL) {\r
- if (currentNewBlock->AddFrame(track, timecode, buffer, *PastBlock, *ForwBlock, lacing)) {\r
- // more data are allowed in this Block\r
- return true;\r
- } else {\r
- currentNewBlock = NULL;\r
- return false;\r
- }\r
- } else {\r
- if (currentNewBlock->AddFrame(track, timecode, buffer, *PastBlock, lacing)) {\r
- // more data are allowed in this Block\r
- return true;\r
- } else {\r
- currentNewBlock = NULL;\r
- return false;\r
- }\r
- }\r
- } else {\r
- if (currentNewBlock->AddFrame(track, timecode, buffer, lacing)) {\r
- // more data are allowed in this Block\r
- return true;\r
- } else {\r
- currentNewBlock = NULL;\r
- return false;\r
- }\r
- }\r
-}\r
-\r
-bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, LacingType lacing)\r
-{\r
- assert(Blobs.size() == 0); // mutually exclusive for the moment\r
- return AddFrameInternal(track, timecode, buffer, MyNewBlock, NULL, NULL, lacing);\r
-}\r
-\r
-bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup & PastBlock, LacingType lacing)\r
-{\r
- assert(Blobs.size() == 0); // mutually exclusive for the moment\r
- return AddFrameInternal(track, timecode, buffer, MyNewBlock, &PastBlock, NULL, lacing);\r
-}\r
-\r
-bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing)\r
-{\r
- assert(Blobs.size() == 0); // mutually exclusive for the moment\r
- return AddFrameInternal(track, timecode, buffer, MyNewBlock, &PastBlock, &ForwBlock, lacing);\r
-}\r
-\r
-/*!\r
- \todo only put the Blocks written in the cue entries\r
-*/\r
-filepos_t KaxCluster::Render(IOCallback & output, KaxCues & CueToUpdate, bool bSaveDefault)\r
-{\r
- filepos_t Result = 0;\r
- size_t TrkIndex, Index;\r
-\r
- // update the Timecode of the Cluster before writing\r
- KaxClusterTimecode * Timecode = static_cast<KaxClusterTimecode *>(this->FindElt(EBML_INFO(KaxClusterTimecode)));\r
- *static_cast<EbmlUInteger *>(Timecode) = GlobalTimecode() / GlobalTimecodeScale();\r
-\r
- if (Blobs.size() == 0) {\r
- // old-school direct KaxBlockGroup\r
-\r
- // SilentTracks handling\r
- // check the parent cluster for existing tracks and see if they are contained in this cluster or not\r
- if (bSilentTracksUsed)\r
- {\r
- KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));\r
- for (TrkIndex = 0; TrkIndex < MyTracks.ListSize(); TrkIndex++) {\r
- if (EbmlId(*MyTracks[TrkIndex]) == EBML_ID(KaxTrackEntry))\r
- {\r
- KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(MyTracks[TrkIndex]);\r
- uint32 tracknum = entry.TrackNumber();\r
- for (Index = 0; Index < ListSize(); Index++) {\r
- if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {\r
- KaxBlockGroup & group = *static_cast<KaxBlockGroup *>((*this)[Index]);\r
- if (group.TrackNumber() == tracknum)\r
- break; // this track is used\r
- }\r
- }\r
- // the track wasn't found in this cluster\r
- if (Index == ListSize())\r
- {\r
- KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));\r
- assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster\r
- KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));\r
- *static_cast<EbmlUInteger *>(trackelt) = tracknum;\r
- }\r
- }\r
- }\r
- }\r
-\r
- Result = EbmlMaster::Render(output, bSaveDefault);\r
- // For all Blocks add their position on the CueEntry\r
- \r
- for (Index = 0; Index < ListSize(); Index++) {\r
- if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {\r
- CueToUpdate.PositionSet(*static_cast<const KaxBlockGroup *>((*this)[Index]));\r
- }\r
- }\r
- } else {\r
- // new school, using KaxBlockBlob\r
- for (Index = 0; Index<Blobs.size(); Index++)\r
- {\r
-#if MATROSKA_VERSION >= 2\r
- if (Blobs[Index]->IsSimpleBlock())\r
- PushElement( (KaxSimpleBlock&) *Blobs[Index] );\r
- else\r
-#endif\r
- PushElement( (KaxBlockGroup&) *Blobs[Index] );\r
- }\r
-\r
- // SilentTracks handling\r
- // check the parent cluster for existing tracks and see if they are contained in this cluster or not\r
- if (bSilentTracksUsed)\r
- {\r
- KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));\r
- for (TrkIndex = 0; TrkIndex < MyTracks.ListSize(); TrkIndex++) {\r
- if (EbmlId(*MyTracks[TrkIndex]) == EBML_ID(KaxTrackEntry))\r
- {\r
- KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(MyTracks[TrkIndex]);\r
- uint32 tracknum = entry.TrackNumber();\r
- for (Index = 0; Index<Blobs.size(); Index++) {\r
- if (((KaxInternalBlock&)*Blobs[Index]).TrackNum() == tracknum)\r
- break; // this track is used\r
- }\r
- // the track wasn't found in this cluster\r
- if (Index == ListSize())\r
- {\r
- KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));\r
- assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster\r
- KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));\r
- *static_cast<EbmlUInteger *>(trackelt) = tracknum;\r
- }\r
- }\r
- }\r
- }\r
-\r
- Result = EbmlMaster::Render(output, bSaveDefault);\r
-\r
- // For all Blocks add their position on the CueEntry\r
- for (Index = 0; Index<Blobs.size(); Index++) {\r
- CueToUpdate.PositionSet(*Blobs[Index]);\r
- }\r
-\r
- Blobs.clear();\r
- }\r
-\r
- return Result;\r
-}\r
-\r
-/*!\r
- \todo automatically choose valid timecode for the Cluster based on the previous cluster timecode (must be incremental)\r
-*/\r
-uint64 KaxCluster::GlobalTimecode() const\r
-{\r
- assert(bPreviousTimecodeIsSet);\r
- uint64 result = MinTimecode;\r
-\r
- if (result < PreviousTimecode)\r
- result = PreviousTimecode + 1;\r
- \r
- return result;\r
-}\r
-\r
-/*!\r
- \brief retrieve the relative \r
- \todo !!! We need a way to know the TimecodeScale\r
-*/\r
-int16 KaxCluster::GetBlockLocalTimecode(uint64 aGlobalTimecode) const\r
-{\r
- int64 TimecodeDelay = (int64(aGlobalTimecode) - int64(GlobalTimecode())) / int64(GlobalTimecodeScale());\r
- assert(TimecodeDelay >= int16(0x8000) && TimecodeDelay <= int16(0x7FFF));\r
- return int16(TimecodeDelay);\r
-}\r
-\r
-uint64 KaxCluster::GetBlockGlobalTimecode(int16 GlobalSavedTimecode)\r
-{\r
- if (!bFirstFrameInside) {\r
- KaxClusterTimecode * Timecode = static_cast<KaxClusterTimecode *>(this->FindElt(EBML_INFO(KaxClusterTimecode)));\r
- assert (bFirstFrameInside); // use the InitTimecode() hack for now\r
- MinTimecode = MaxTimecode = PreviousTimecode = *static_cast<EbmlUInteger *>(Timecode);\r
- bFirstFrameInside = true;\r
- bPreviousTimecodeIsSet = true;\r
- }\r
- return int64(GlobalSavedTimecode * GlobalTimecodeScale()) + GlobalTimecode();\r
-}\r
-\r
-KaxBlockGroup & KaxCluster::GetNewBlock()\r
-{\r
- KaxBlockGroup & MyBlock = AddNewChild<KaxBlockGroup>(*this);\r
- MyBlock.SetParent(*this);\r
- return MyBlock;\r
-}\r
-\r
-void KaxCluster::ReleaseFrames()\r
-{\r
- size_t Index;\r
- \r
- for (Index = 0; Index < ListSize(); Index++) {\r
- if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {\r
- static_cast<KaxBlockGroup*>((*this)[Index])->ReleaseFrames();\r
- }\r
- }\r
-}\r
-\r
-uint64 KaxCluster::GetPosition() const\r
-{\r
- assert(ParentSegment != NULL);\r
- return ParentSegment->GetRelativePosition(*this);\r
-}\r
-\r
-END_LIBMATROSKA_NAMESPACE\r
+/****************************************************************************
+** libmatroska : parse Matroska files, see http://www.matroska.org/
+**
+** <file/class description>
+**
+** Copyright (C) 2002-2010 Steve Lhomme. All rights reserved.
+**
+** 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: KaxCluster.cpp 1228 2005-10-14 19:36:51Z robux4 $
+ \author Steve Lhomme <robux4 @ users.sf.net>
+*/
+#include "matroska/KaxCluster.h"
+#include "matroska/KaxClusterData.h"
+#include "matroska/KaxBlock.h"
+#include "matroska/KaxContexts.h"
+#include "matroska/KaxSegment.h"
+#include "matroska/KaxDefines.h"
+
+// sub elements
+START_LIBMATROSKA_NAMESPACE
+
+DEFINE_START_SEMANTIC(KaxCluster)
+DEFINE_SEMANTIC_ITEM(true, true, KaxClusterTimecode)
+DEFINE_SEMANTIC_ITEM(false, true, KaxClusterSilentTracks)
+DEFINE_SEMANTIC_ITEM(false, true, KaxClusterPrevSize)
+DEFINE_SEMANTIC_ITEM(false, false, KaxBlockGroup)
+#if MATROSKA_VERSION == 2
+DEFINE_SEMANTIC_ITEM(false, false, KaxSimpleBlock)
+#endif
+DEFINE_SEMANTIC_ITEM(false, true, KaxClusterPosition)
+DEFINE_END_SEMANTIC(KaxCluster)
+
+DEFINE_MKX_MASTER_CONS(KaxCluster, 0x1F43B675, 4, KaxSegment, "Cluster");
+
+KaxCluster::KaxCluster()
+ :EbmlMaster(EBML_CLASS_SEMCONTEXT(KaxCluster))
+ ,currentNewBlock(NULL)
+ ,ParentSegment(NULL)
+ ,bFirstFrameInside(false)
+ ,bPreviousTimecodeIsSet(false)
+ ,bTimecodeScaleIsSet(false)
+ ,bSilentTracksUsed(false)
+{}
+
+KaxCluster::KaxCluster(const KaxCluster & ElementToClone)
+ :EbmlMaster(ElementToClone)
+ ,bSilentTracksUsed(ElementToClone.bSilentTracksUsed)
+{
+ // update the parent of each children
+ EBML_MASTER_ITERATOR Itr = begin();
+ while (Itr != end())
+ {
+ if (EbmlId(**Itr) == EBML_ID(KaxBlockGroup)) {
+ static_cast<KaxBlockGroup *>(*Itr)->SetParent(*this);
+ } else if (EbmlId(**Itr) == EBML_ID(KaxBlock)) {
+ static_cast<KaxBlock *>(*Itr)->SetParent(*this);
+#if MATROSKA_VERSION >= 2
+ } else if (EbmlId(**Itr) == EBML_ID(KaxBlockVirtual)) {
+ static_cast<KaxBlockVirtual *>(*Itr)->SetParent(*this);
+#endif // MATROSKA_VERSION
+ }
+ ++Itr;
+ }
+}
+
+bool KaxCluster::AddBlockBlob(KaxBlockBlob * NewBlob)
+{
+ Blobs.push_back(NewBlob);
+ return true;
+}
+
+bool KaxCluster::AddFrameInternal(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup * PastBlock, const KaxBlockGroup * ForwBlock, LacingType lacing)
+{
+ if (!bFirstFrameInside) {
+ bFirstFrameInside = true;
+ MinTimecode = MaxTimecode = timecode;
+ } else {
+ if (timecode < MinTimecode)
+ MinTimecode = timecode;
+ if (timecode > MaxTimecode)
+ MaxTimecode = timecode;
+ }
+
+ MyNewBlock = NULL;
+
+ if (lacing == LACING_NONE || !track.LacingEnabled()) {
+ currentNewBlock = NULL;
+ }
+
+ // force creation of a new block
+ if (currentNewBlock == NULL || uint32(track.TrackNumber()) != uint32(currentNewBlock->TrackNumber()) || PastBlock != NULL || ForwBlock != NULL) {
+ KaxBlockGroup & aNewBlock = GetNewBlock();
+ MyNewBlock = currentNewBlock = &aNewBlock;
+ currentNewBlock = &aNewBlock;
+ }
+
+ if (PastBlock != NULL) {
+ if (ForwBlock != NULL) {
+ if (currentNewBlock->AddFrame(track, timecode, buffer, *PastBlock, *ForwBlock, lacing)) {
+ // more data are allowed in this Block
+ return true;
+ } else {
+ currentNewBlock = NULL;
+ return false;
+ }
+ } else {
+ if (currentNewBlock->AddFrame(track, timecode, buffer, *PastBlock, lacing)) {
+ // more data are allowed in this Block
+ return true;
+ } else {
+ currentNewBlock = NULL;
+ return false;
+ }
+ }
+ } else {
+ if (currentNewBlock->AddFrame(track, timecode, buffer, lacing)) {
+ // more data are allowed in this Block
+ return true;
+ } else {
+ currentNewBlock = NULL;
+ return false;
+ }
+ }
+}
+
+bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, LacingType lacing)
+{
+ assert(Blobs.size() == 0); // mutually exclusive for the moment
+ return AddFrameInternal(track, timecode, buffer, MyNewBlock, NULL, NULL, lacing);
+}
+
+bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup & PastBlock, LacingType lacing)
+{
+ assert(Blobs.size() == 0); // mutually exclusive for the moment
+ return AddFrameInternal(track, timecode, buffer, MyNewBlock, &PastBlock, NULL, lacing);
+}
+
+bool KaxCluster::AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, KaxBlockGroup * & MyNewBlock, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing)
+{
+ assert(Blobs.size() == 0); // mutually exclusive for the moment
+ return AddFrameInternal(track, timecode, buffer, MyNewBlock, &PastBlock, &ForwBlock, lacing);
+}
+
+/*!
+ \todo only put the Blocks written in the cue entries
+*/
+filepos_t KaxCluster::Render(IOCallback & output, KaxCues & CueToUpdate, bool bSaveDefault)
+{
+ filepos_t Result = 0;
+ size_t TrkIndex, Index;
+
+ // update the Timecode of the Cluster before writing
+ KaxClusterTimecode * Timecode = static_cast<KaxClusterTimecode *>(this->FindElt(EBML_INFO(KaxClusterTimecode)));
+ *static_cast<EbmlUInteger *>(Timecode) = GlobalTimecode() / GlobalTimecodeScale();
+
+ if (Blobs.size() == 0) {
+ // old-school direct KaxBlockGroup
+
+ // SilentTracks handling
+ // check the parent cluster for existing tracks and see if they are contained in this cluster or not
+ if (bSilentTracksUsed)
+ {
+ KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));
+ for (TrkIndex = 0; TrkIndex < MyTracks.ListSize(); TrkIndex++) {
+ if (EbmlId(*MyTracks[TrkIndex]) == EBML_ID(KaxTrackEntry))
+ {
+ KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(MyTracks[TrkIndex]);
+ uint32 tracknum = entry.TrackNumber();
+ for (Index = 0; Index < ListSize(); Index++) {
+ if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {
+ KaxBlockGroup & group = *static_cast<KaxBlockGroup *>((*this)[Index]);
+ if (group.TrackNumber() == tracknum)
+ break; // this track is used
+ }
+ }
+ // the track wasn't found in this cluster
+ if (Index == ListSize())
+ {
+ KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));
+ assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster
+ KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));
+ *static_cast<EbmlUInteger *>(trackelt) = tracknum;
+ }
+ }
+ }
+ }
+
+ Result = EbmlMaster::Render(output, bSaveDefault);
+ // For all Blocks add their position on the CueEntry
+
+ for (Index = 0; Index < ListSize(); Index++) {
+ if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {
+ CueToUpdate.PositionSet(*static_cast<const KaxBlockGroup *>((*this)[Index]));
+ }
+ }
+ } else {
+ // new school, using KaxBlockBlob
+ for (Index = 0; Index<Blobs.size(); Index++)
+ {
+#if MATROSKA_VERSION >= 2
+ if (Blobs[Index]->IsSimpleBlock())
+ PushElement( (KaxSimpleBlock&) *Blobs[Index] );
+ else
+#endif
+ PushElement( (KaxBlockGroup&) *Blobs[Index] );
+ }
+
+ // SilentTracks handling
+ // check the parent cluster for existing tracks and see if they are contained in this cluster or not
+ if (bSilentTracksUsed)
+ {
+ KaxTracks & MyTracks = *static_cast<KaxTracks *>(ParentSegment->FindElt(EBML_INFO(KaxTracks)));
+ for (TrkIndex = 0; TrkIndex < MyTracks.ListSize(); TrkIndex++) {
+ if (EbmlId(*MyTracks[TrkIndex]) == EBML_ID(KaxTrackEntry))
+ {
+ KaxTrackEntry & entry = *static_cast<KaxTrackEntry *>(MyTracks[TrkIndex]);
+ uint32 tracknum = entry.TrackNumber();
+ for (Index = 0; Index<Blobs.size(); Index++) {
+ if (((KaxInternalBlock&)*Blobs[Index]).TrackNum() == tracknum)
+ break; // this track is used
+ }
+ // the track wasn't found in this cluster
+ if (Index == ListSize())
+ {
+ KaxClusterSilentTracks * SilentTracks = static_cast<KaxClusterSilentTracks *>(this->FindFirstElt(EBML_INFO(KaxClusterSilentTracks)));
+ assert(SilentTracks != NULL); // the flag bSilentTracksUsed should be set when creating the Cluster
+ KaxClusterSilentTrackNumber * trackelt = static_cast<KaxClusterSilentTrackNumber *>(SilentTracks->AddNewElt(EBML_INFO(KaxClusterSilentTrackNumber)));
+ *static_cast<EbmlUInteger *>(trackelt) = tracknum;
+ }
+ }
+ }
+ }
+
+ Result = EbmlMaster::Render(output, bSaveDefault);
+
+ // For all Blocks add their position on the CueEntry
+ for (Index = 0; Index<Blobs.size(); Index++) {
+ CueToUpdate.PositionSet(*Blobs[Index]);
+ }
+
+ Blobs.clear();
+ }
+
+ return Result;
+}
+
+/*!
+ \todo automatically choose valid timecode for the Cluster based on the previous cluster timecode (must be incremental)
+*/
+uint64 KaxCluster::GlobalTimecode() const
+{
+ assert(bPreviousTimecodeIsSet);
+ uint64 result = MinTimecode;
+
+ if (result < PreviousTimecode)
+ result = PreviousTimecode + 1;
+
+ return result;
+}
+
+/*!
+ \brief retrieve the relative
+ \todo !!! We need a way to know the TimecodeScale
+*/
+int16 KaxCluster::GetBlockLocalTimecode(uint64 aGlobalTimecode) const
+{
+ int64 TimecodeDelay = (int64(aGlobalTimecode) - int64(GlobalTimecode())) / int64(GlobalTimecodeScale());
+ assert(TimecodeDelay >= int16(0x8000) && TimecodeDelay <= int16(0x7FFF));
+ return int16(TimecodeDelay);
+}
+
+uint64 KaxCluster::GetBlockGlobalTimecode(int16 GlobalSavedTimecode)
+{
+ if (!bFirstFrameInside) {
+ KaxClusterTimecode * Timecode = static_cast<KaxClusterTimecode *>(this->FindElt(EBML_INFO(KaxClusterTimecode)));
+ assert (bFirstFrameInside); // use the InitTimecode() hack for now
+ MinTimecode = MaxTimecode = PreviousTimecode = *static_cast<EbmlUInteger *>(Timecode);
+ bFirstFrameInside = true;
+ bPreviousTimecodeIsSet = true;
+ }
+ return int64(GlobalSavedTimecode * GlobalTimecodeScale()) + GlobalTimecode();
+}
+
+KaxBlockGroup & KaxCluster::GetNewBlock()
+{
+ KaxBlockGroup & MyBlock = AddNewChild<KaxBlockGroup>(*this);
+ MyBlock.SetParent(*this);
+ return MyBlock;
+}
+
+void KaxCluster::ReleaseFrames()
+{
+ size_t Index;
+
+ for (Index = 0; Index < ListSize(); Index++) {
+ if (EbmlId(*(*this)[Index]) == EBML_ID(KaxBlockGroup)) {
+ static_cast<KaxBlockGroup*>((*this)[Index])->ReleaseFrames();
+ }
+ }
+}
+
+uint64 KaxCluster::GetPosition() const
+{
+ assert(ParentSegment != NULL);
+ return ParentSegment->GetRelativePosition(*this);
+}
+
+END_LIBMATROSKA_NAMESPACE