From: Steve Lhomme Date: Thu, 24 Jun 2010 16:21:14 +0000 (+0000) Subject: libmatroska: give access to the SetParent to KaxSimpleBlock as well X-Git-Tag: release-1.1.0~22 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0f82b00c63b5b92a0428afcbee7cedd4c889b746;p=libmatroska libmatroska: give access to the SetParent to KaxSimpleBlock as well git-svn-id: https://matroska.svn.sourceforge.net/svnroot/matroska/trunk/libmatroska@336 a6f86f6d-0131-4f8e-9e7b-e335508773d5 --- diff --git a/libmatroska.proj b/libmatroska.proj index 769c701..559a95e 100644 --- a/libmatroska.proj +++ b/libmatroska.proj @@ -2,7 +2,7 @@ Include "*/*.proj" LIB matroska { - PROJECT_VERSION 1.0.0 + PROJECT_VERSION 1.1.0 INCLUDE . EXPINCLUDE . diff --git a/matroska/KaxBlock.h b/matroska/KaxBlock.h index 1c62152..a60a4f2 100644 --- a/matroska/KaxBlock.h +++ b/matroska/KaxBlock.h @@ -1,383 +1,384 @@ -/**************************************************************************** -** libmatroska : parse Matroska files, see http://www.matroska.org/ -** -** -** -** 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 - \todo add a PureBlock class to group functionalities between Block and BlockVirtual - \version \$Id: KaxBlock.h,v 1.24 2004/04/14 23:26:17 robux4 Exp $ - \author Steve Lhomme - \author Julien Coloos -*/ -#ifndef LIBMATROSKA_BLOCK_H -#define LIBMATROSKA_BLOCK_H - -#include - -#include "matroska/KaxTypes.h" -#include "ebml/EbmlBinary.h" -#include "ebml/EbmlMaster.h" -#include "matroska/KaxTracks.h" -#include "matroska/KaxDefines.h" - -using namespace LIBEBML_NAMESPACE; - -START_LIBMATROSKA_NAMESPACE - -class KaxCluster; -class KaxReferenceBlock; -class KaxInternalBlock; -class KaxBlockBlob; - -class MATROSKA_DLL_API DataBuffer { - protected: - binary * myBuffer; - uint32 mySize; - bool bValidValue; - bool (*myFreeBuffer)(const DataBuffer & aBuffer); // method to free the internal buffer - - public: - DataBuffer(binary * aBuffer, uint32 aSize, bool (*aFreeBuffer)(const DataBuffer & aBuffer) = NULL) - :myBuffer(aBuffer) - ,mySize(aSize) - ,bValidValue(true) - ,myFreeBuffer(aFreeBuffer) - {} - virtual ~DataBuffer() {} - virtual binary * Buffer() {return myBuffer;} - virtual uint32 & Size() {return mySize;}; - virtual const binary * Buffer() const {return myBuffer;} - virtual const uint32 Size() const {return mySize;}; - bool FreeBuffer(const DataBuffer & aBuffer) { - bool bResult = true; - if (myBuffer != NULL && myFreeBuffer != NULL && bValidValue) { - bResult = myFreeBuffer(aBuffer); - myBuffer = NULL; - bValidValue = false; - } - return bResult; - } - - virtual DataBuffer * Clone(); -}; - -class MATROSKA_DLL_API SimpleDataBuffer : public DataBuffer { - public: - SimpleDataBuffer(binary * aBuffer, uint32 aSize, uint32 aOffset, bool (*aFreeBuffer)(const DataBuffer & aBuffer) = myFreeBuffer) - :DataBuffer(aBuffer + aOffset, aSize, aFreeBuffer) - ,Offset(aOffset) - ,BaseBuffer(aBuffer) - {} - virtual ~SimpleDataBuffer() {} - - DataBuffer * Clone() {return new SimpleDataBuffer(*this);} - - protected: - uint32 Offset; - binary * BaseBuffer; - - static bool myFreeBuffer(const DataBuffer & aBuffer) - { - binary *_Buffer = static_cast(&aBuffer)->BaseBuffer; - if (_Buffer != NULL) - free(_Buffer); - return true; - } - - SimpleDataBuffer(const SimpleDataBuffer & ToClone); -}; - -/*! - \note the data is copied locally, it can be freed right away -* / -class MATROSKA_DLL_API NotSoSimpleDataBuffer : public SimpleDataBuffer { - public: - NotSoSimpleDataBuffer(binary * aBuffer, uint32 aSize, uint32 aOffset) - :SimpleDataBuffer(new binary[aSize - aOffset], aSize, 0) - { - memcpy(BaseBuffer, aBuffer + aOffset, aSize - aOffset); - } -}; -*/ - -DECLARE_MKX_MASTER(KaxBlockGroup) - public: - ~KaxBlockGroup(); - - /*! - \brief Addition of a frame without references - */ - bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO); - /*! - \brief Addition of a frame with a backward reference (P frame) - */ - bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, LacingType lacing = LACING_AUTO); - - /*! - \brief Addition of a frame with a backward+forward reference (B frame) - */ - bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing = LACING_AUTO); - bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock, LacingType lacing = LACING_AUTO); - - void SetParent(KaxCluster & aParentCluster); - - void SetParentTrack(const KaxTrackEntry & aParentTrack) { - ParentTrack = &aParentTrack; - } - - /*! - \brief Set the duration of the contained frame(s) (for the total number of frames) - */ - void SetBlockDuration(uint64 TimeLength); - bool GetBlockDuration(uint64 &TheTimecode) const; - - /*! - \return the global timecode of this Block (not just the delta to the Cluster) - */ - uint64 GlobalTimecode() const; - uint64 GlobalTimecodeScale() const { - assert(ParentTrack != NULL); - return ParentTrack->GlobalTimecodeScale(); - } - - uint16 TrackNumber() const; - - uint64 ClusterPosition() const; - - /*! - \return the number of references to other frames - */ - unsigned int ReferenceCount() const; - const KaxReferenceBlock & Reference(unsigned int Index) const; - - /*! - \brief release all the frames of all Blocks - */ - void ReleaseFrames(); - - operator KaxInternalBlock &(); - - const KaxCluster *GetParentCluster() const { return ParentCluster; } - - protected: - KaxCluster * ParentCluster; - const KaxTrackEntry * ParentTrack; -}; - -class KaxInternalBlock : public EbmlBinary { - public: - KaxInternalBlock(EBML_DEF_CONS EBML_DEF_SEP bool bSimple EBML_DEF_SEP EBML_EXTRA_PARAM) :EBML_DEF_BINARY_INIT EBML_DEF_SEP bLocalTimecodeUsed(false), mLacing(LACING_AUTO), mInvisible(false) - ,ParentCluster(NULL), bIsSimple(bSimple), bIsKeyframe(true), bIsDiscardable(false) - {} - KaxInternalBlock(const KaxInternalBlock & ElementToClone); - ~KaxInternalBlock(); - virtual bool ValidateSize() const; - - uint16 TrackNum() const {return TrackNumber;} - /*! - \todo !!!! This method needs to be changes ! - */ - uint64 GlobalTimecode() const {return Timecode;} - - /*! - \note override this function to generate the Data/Size on the fly, unlike the usual binary elements - */ - filepos_t UpdateSize(bool bSaveDefault = false, bool bForceRender = false); - filepos_t ReadData(IOCallback & input, ScopeMode ReadFully = SCOPE_ALL_DATA); - - /*! - \brief Only read the head of the Block (not internal data) - \note convenient when you are parsing the file quickly - */ - uint64 ReadInternalHead(IOCallback & input); - - unsigned int NumberFrames() const { return SizeList.size();} - DataBuffer & GetBuffer(unsigned int iIndex) {return *myBuffers[iIndex];} - - bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO, bool invisible = false); - - /*! - \brief release all the frames of all Blocks - */ - void ReleaseFrames(); - - void SetParent(KaxCluster & aParentCluster); - - /*! - \return Returns the lacing type that produces the smallest footprint. - */ - LacingType GetBestLacingType() const; - - /*! - \param FrameNumber 0 for the first frame - \return the position in the stream for a given frame - \note return -1 if the position doesn't exist - */ - int64 GetDataPosition(size_t FrameNumber = 0); - - /*! - \param FrameNumber 0 for the first frame - \return the size of a given frame - \note return -1 if the position doesn't exist - */ - int64 GetFrameSize(size_t FrameNumber = 0); - - bool IsInvisible() const { return mInvisible; } - - uint64 ClusterPosition() const; - - protected: - std::vector myBuffers; - std::vector SizeList; - uint64 Timecode; // temporary timecode of the first frame, non scaled - int16 LocalTimecode; - bool bLocalTimecodeUsed; - uint16 TrackNumber; - LacingType mLacing; - bool mInvisible; - uint64 FirstFrameLocation; - - filepos_t RenderData(IOCallback & output, bool bForceRender, bool bSaveDefault = false); - - KaxCluster * ParentCluster; - bool bIsSimple; - bool bIsKeyframe; - bool bIsDiscardable; -}; - -DECLARE_MKX_CONTEXT(KaxBlock); -class MATROSKA_DLL_API KaxBlock : public KaxInternalBlock { - public: - KaxBlock(EBML_EXTRA_PARAM) :KaxInternalBlock(EBML_DEF_BINARY_CTX(KaxBlock)EBML_DEF_SEP false EBML_DEF_SEP EBML_EXTRA_CALL) {} - EBML_CONCRETE_CLASS(KaxBlock) -}; - -#if MATROSKA_VERSION >= 2 -DECLARE_MKX_CONTEXT(KaxSimpleBlock); -class MATROSKA_DLL_API KaxSimpleBlock : public KaxInternalBlock { - public: - KaxSimpleBlock(EBML_EXTRA_PARAM) :KaxInternalBlock(EBML_DEF_BINARY_CTX(KaxSimpleBlock)EBML_DEF_SEP true EBML_DEF_SEP EBML_EXTRA_CALL) {} - - void SetKeyframe(bool b_keyframe) { bIsKeyframe = b_keyframe; } - void SetDiscardable(bool b_discard) { bIsDiscardable = b_discard; } - - bool IsKeyframe() const { return bIsKeyframe; } - bool IsDiscardable() const { return bIsDiscardable; } - - operator KaxInternalBlock &() { return *this; } - - EBML_CONCRETE_CLASS(KaxSimpleBlock) -}; -#endif // MATROSKA_VERSION - -class MATROSKA_DLL_API KaxBlockBlob { -public: - KaxBlockBlob(BlockBlobType sblock_mode) :ParentCluster(NULL), SimpleBlockMode(sblock_mode) { - bUseSimpleBlock = (sblock_mode != BLOCK_BLOB_NO_SIMPLE); - Block.group = NULL; - } - - ~KaxBlockBlob() { -#if MATROSKA_VERSION >= 2 - if (bUseSimpleBlock) - delete Block.simpleblock; - else -#endif // MATROSKA_VERSION - delete Block.group; - } - - operator KaxBlockGroup &(); - operator const KaxBlockGroup &() const; -#if MATROSKA_VERSION >= 2 - operator KaxSimpleBlock &(); -#endif - operator KaxInternalBlock &(); - operator const KaxInternalBlock &() const; - - void SetBlockGroup( KaxBlockGroup &BlockRef ); - - void SetBlockDuration(uint64 TimeLength); - - void SetParent(KaxCluster & aParentCluster); - bool AddFrameAuto(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO, const KaxBlockBlob * PastBlock = NULL, const KaxBlockBlob * ForwBlock = NULL); - - bool IsSimpleBlock() const {return bUseSimpleBlock;} - - bool ReplaceSimpleByGroup(); -protected: - KaxCluster * ParentCluster; - union { - KaxBlockGroup *group; -#if MATROSKA_VERSION >= 2 - KaxSimpleBlock *simpleblock; -#endif // MATROSKA_VERSION - } Block; - bool bUseSimpleBlock; - BlockBlobType SimpleBlockMode; -}; - -DECLARE_MKX_UINTEGER(KaxBlockDuration) -}; - -#if MATROSKA_VERSION >= 2 -DECLARE_MKX_BINARY_CONS(KaxBlockVirtual) - public: - ~KaxBlockVirtual(); - - /*! - \note override this function to generate the Data/Size on the fly, unlike the usual binary elements - */ - filepos_t UpdateSize(bool bSaveDefault = false, bool bForceRender = false); - - void SetParent(const KaxCluster & aParentCluster) {ParentCluster = &aParentCluster;} - - protected: - uint64 Timecode; // temporary timecode of the first frame if there are more than one - uint16 TrackNumber; - binary DataBlock[5]; - - const KaxCluster * ParentCluster; -}; -#endif // MATROSKA_VERSION - -DECLARE_MKX_BINARY(KaxBlockAdditional) -}; - -DECLARE_MKX_MASTER(KaxBlockAdditions) -}; - -DECLARE_MKX_MASTER(KaxBlockMore) -}; - -DECLARE_MKX_UINTEGER(KaxBlockAddID) -}; - -DECLARE_MKX_BINARY(KaxCodecState) -}; - -END_LIBMATROSKA_NAMESPACE - -#endif // LIBMATROSKA_BLOCK_H +/**************************************************************************** +** libmatroska : parse Matroska files, see http://www.matroska.org/ +** +** +** +** 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 + \todo add a PureBlock class to group functionalities between Block and BlockVirtual + \version \$Id: KaxBlock.h,v 1.24 2004/04/14 23:26:17 robux4 Exp $ + \author Steve Lhomme + \author Julien Coloos +*/ +#ifndef LIBMATROSKA_BLOCK_H +#define LIBMATROSKA_BLOCK_H + +#include + +#include "matroska/KaxTypes.h" +#include "ebml/EbmlBinary.h" +#include "ebml/EbmlMaster.h" +#include "matroska/KaxTracks.h" +#include "matroska/KaxDefines.h" + +using namespace LIBEBML_NAMESPACE; + +START_LIBMATROSKA_NAMESPACE + +class KaxCluster; +class KaxReferenceBlock; +class KaxInternalBlock; +class KaxBlockBlob; + +class MATROSKA_DLL_API DataBuffer { + protected: + binary * myBuffer; + uint32 mySize; + bool bValidValue; + bool (*myFreeBuffer)(const DataBuffer & aBuffer); // method to free the internal buffer + + public: + DataBuffer(binary * aBuffer, uint32 aSize, bool (*aFreeBuffer)(const DataBuffer & aBuffer) = NULL) + :myBuffer(aBuffer) + ,mySize(aSize) + ,bValidValue(true) + ,myFreeBuffer(aFreeBuffer) + {} + virtual ~DataBuffer() {} + virtual binary * Buffer() {return myBuffer;} + virtual uint32 & Size() {return mySize;}; + virtual const binary * Buffer() const {return myBuffer;} + virtual const uint32 Size() const {return mySize;}; + bool FreeBuffer(const DataBuffer & aBuffer) { + bool bResult = true; + if (myBuffer != NULL && myFreeBuffer != NULL && bValidValue) { + bResult = myFreeBuffer(aBuffer); + myBuffer = NULL; + bValidValue = false; + } + return bResult; + } + + virtual DataBuffer * Clone(); +}; + +class MATROSKA_DLL_API SimpleDataBuffer : public DataBuffer { + public: + SimpleDataBuffer(binary * aBuffer, uint32 aSize, uint32 aOffset, bool (*aFreeBuffer)(const DataBuffer & aBuffer) = myFreeBuffer) + :DataBuffer(aBuffer + aOffset, aSize, aFreeBuffer) + ,Offset(aOffset) + ,BaseBuffer(aBuffer) + {} + virtual ~SimpleDataBuffer() {} + + DataBuffer * Clone() {return new SimpleDataBuffer(*this);} + + protected: + uint32 Offset; + binary * BaseBuffer; + + static bool myFreeBuffer(const DataBuffer & aBuffer) + { + binary *_Buffer = static_cast(&aBuffer)->BaseBuffer; + if (_Buffer != NULL) + free(_Buffer); + return true; + } + + SimpleDataBuffer(const SimpleDataBuffer & ToClone); +}; + +/*! + \note the data is copied locally, it can be freed right away +* / +class MATROSKA_DLL_API NotSoSimpleDataBuffer : public SimpleDataBuffer { + public: + NotSoSimpleDataBuffer(binary * aBuffer, uint32 aSize, uint32 aOffset) + :SimpleDataBuffer(new binary[aSize - aOffset], aSize, 0) + { + memcpy(BaseBuffer, aBuffer + aOffset, aSize - aOffset); + } +}; +*/ + +DECLARE_MKX_MASTER(KaxBlockGroup) + public: + ~KaxBlockGroup(); + + /*! + \brief Addition of a frame without references + */ + bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO); + /*! + \brief Addition of a frame with a backward reference (P frame) + */ + bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, LacingType lacing = LACING_AUTO); + + /*! + \brief Addition of a frame with a backward+forward reference (B frame) + */ + bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockGroup & PastBlock, const KaxBlockGroup & ForwBlock, LacingType lacing = LACING_AUTO); + bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, const KaxBlockBlob * PastBlock, const KaxBlockBlob * ForwBlock, LacingType lacing = LACING_AUTO); + + void SetParent(KaxCluster & aParentCluster); + + void SetParentTrack(const KaxTrackEntry & aParentTrack) { + ParentTrack = &aParentTrack; + } + + /*! + \brief Set the duration of the contained frame(s) (for the total number of frames) + */ + void SetBlockDuration(uint64 TimeLength); + bool GetBlockDuration(uint64 &TheTimecode) const; + + /*! + \return the global timecode of this Block (not just the delta to the Cluster) + */ + uint64 GlobalTimecode() const; + uint64 GlobalTimecodeScale() const { + assert(ParentTrack != NULL); + return ParentTrack->GlobalTimecodeScale(); + } + + uint16 TrackNumber() const; + + uint64 ClusterPosition() const; + + /*! + \return the number of references to other frames + */ + unsigned int ReferenceCount() const; + const KaxReferenceBlock & Reference(unsigned int Index) const; + + /*! + \brief release all the frames of all Blocks + */ + void ReleaseFrames(); + + operator KaxInternalBlock &(); + + const KaxCluster *GetParentCluster() const { return ParentCluster; } + + protected: + KaxCluster * ParentCluster; + const KaxTrackEntry * ParentTrack; +}; + +class KaxInternalBlock : public EbmlBinary { + public: + KaxInternalBlock(EBML_DEF_CONS EBML_DEF_SEP bool bSimple EBML_DEF_SEP EBML_EXTRA_PARAM) :EBML_DEF_BINARY_INIT EBML_DEF_SEP bLocalTimecodeUsed(false), mLacing(LACING_AUTO), mInvisible(false) + ,ParentCluster(NULL), bIsSimple(bSimple), bIsKeyframe(true), bIsDiscardable(false) + {} + KaxInternalBlock(const KaxInternalBlock & ElementToClone); + ~KaxInternalBlock(); + virtual bool ValidateSize() const; + + uint16 TrackNum() const {return TrackNumber;} + /*! + \todo !!!! This method needs to be changes ! + */ + uint64 GlobalTimecode() const {return Timecode;} + + /*! + \note override this function to generate the Data/Size on the fly, unlike the usual binary elements + */ + filepos_t UpdateSize(bool bSaveDefault = false, bool bForceRender = false); + filepos_t ReadData(IOCallback & input, ScopeMode ReadFully = SCOPE_ALL_DATA); + + /*! + \brief Only read the head of the Block (not internal data) + \note convenient when you are parsing the file quickly + */ + uint64 ReadInternalHead(IOCallback & input); + + unsigned int NumberFrames() const { return SizeList.size();} + DataBuffer & GetBuffer(unsigned int iIndex) {return *myBuffers[iIndex];} + + bool AddFrame(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO, bool invisible = false); + + /*! + \brief release all the frames of all Blocks + */ + void ReleaseFrames(); + + void SetParent(KaxCluster & aParentCluster); + + /*! + \return Returns the lacing type that produces the smallest footprint. + */ + LacingType GetBestLacingType() const; + + /*! + \param FrameNumber 0 for the first frame + \return the position in the stream for a given frame + \note return -1 if the position doesn't exist + */ + int64 GetDataPosition(size_t FrameNumber = 0); + + /*! + \param FrameNumber 0 for the first frame + \return the size of a given frame + \note return -1 if the position doesn't exist + */ + int64 GetFrameSize(size_t FrameNumber = 0); + + bool IsInvisible() const { return mInvisible; } + + uint64 ClusterPosition() const; + + protected: + std::vector myBuffers; + std::vector SizeList; + uint64 Timecode; // temporary timecode of the first frame, non scaled + int16 LocalTimecode; + bool bLocalTimecodeUsed; + uint16 TrackNumber; + LacingType mLacing; + bool mInvisible; + uint64 FirstFrameLocation; + + filepos_t RenderData(IOCallback & output, bool bForceRender, bool bSaveDefault = false); + + KaxCluster * ParentCluster; + bool bIsSimple; + bool bIsKeyframe; + bool bIsDiscardable; +}; + +DECLARE_MKX_CONTEXT(KaxBlock); +class MATROSKA_DLL_API KaxBlock : public KaxInternalBlock { + public: + KaxBlock(EBML_EXTRA_PARAM) :KaxInternalBlock(EBML_DEF_BINARY_CTX(KaxBlock)EBML_DEF_SEP false EBML_DEF_SEP EBML_EXTRA_CALL) {} + EBML_CONCRETE_CLASS(KaxBlock) +}; + +#if MATROSKA_VERSION >= 2 +DECLARE_MKX_CONTEXT(KaxSimpleBlock); +class MATROSKA_DLL_API KaxSimpleBlock : public KaxInternalBlock { + public: + KaxSimpleBlock(EBML_EXTRA_PARAM) :KaxInternalBlock(EBML_DEF_BINARY_CTX(KaxSimpleBlock)EBML_DEF_SEP true EBML_DEF_SEP EBML_EXTRA_CALL) {} + + void SetKeyframe(bool b_keyframe) { bIsKeyframe = b_keyframe; } + void SetDiscardable(bool b_discard) { bIsDiscardable = b_discard; } + + bool IsKeyframe() const { return bIsKeyframe; } + bool IsDiscardable() const { return bIsDiscardable; } + + operator KaxInternalBlock &() { return *this; } + void SetParent(KaxCluster & aParentCluster); + + EBML_CONCRETE_CLASS(KaxSimpleBlock) +}; +#endif // MATROSKA_VERSION + +class MATROSKA_DLL_API KaxBlockBlob { +public: + KaxBlockBlob(BlockBlobType sblock_mode) :ParentCluster(NULL), SimpleBlockMode(sblock_mode) { + bUseSimpleBlock = (sblock_mode != BLOCK_BLOB_NO_SIMPLE); + Block.group = NULL; + } + + ~KaxBlockBlob() { +#if MATROSKA_VERSION >= 2 + if (bUseSimpleBlock) + delete Block.simpleblock; + else +#endif // MATROSKA_VERSION + delete Block.group; + } + + operator KaxBlockGroup &(); + operator const KaxBlockGroup &() const; +#if MATROSKA_VERSION >= 2 + operator KaxSimpleBlock &(); +#endif + operator KaxInternalBlock &(); + operator const KaxInternalBlock &() const; + + void SetBlockGroup( KaxBlockGroup &BlockRef ); + + void SetBlockDuration(uint64 TimeLength); + + void SetParent(KaxCluster & aParentCluster); + bool AddFrameAuto(const KaxTrackEntry & track, uint64 timecode, DataBuffer & buffer, LacingType lacing = LACING_AUTO, const KaxBlockBlob * PastBlock = NULL, const KaxBlockBlob * ForwBlock = NULL); + + bool IsSimpleBlock() const {return bUseSimpleBlock;} + + bool ReplaceSimpleByGroup(); +protected: + KaxCluster * ParentCluster; + union { + KaxBlockGroup *group; +#if MATROSKA_VERSION >= 2 + KaxSimpleBlock *simpleblock; +#endif // MATROSKA_VERSION + } Block; + bool bUseSimpleBlock; + BlockBlobType SimpleBlockMode; +}; + +DECLARE_MKX_UINTEGER(KaxBlockDuration) +}; + +#if MATROSKA_VERSION >= 2 +DECLARE_MKX_BINARY_CONS(KaxBlockVirtual) + public: + ~KaxBlockVirtual(); + + /*! + \note override this function to generate the Data/Size on the fly, unlike the usual binary elements + */ + filepos_t UpdateSize(bool bSaveDefault = false, bool bForceRender = false); + + void SetParent(const KaxCluster & aParentCluster) {ParentCluster = &aParentCluster;} + + protected: + uint64 Timecode; // temporary timecode of the first frame if there are more than one + uint16 TrackNumber; + binary DataBlock[5]; + + const KaxCluster * ParentCluster; +}; +#endif // MATROSKA_VERSION + +DECLARE_MKX_BINARY(KaxBlockAdditional) +}; + +DECLARE_MKX_MASTER(KaxBlockAdditions) +}; + +DECLARE_MKX_MASTER(KaxBlockMore) +}; + +DECLARE_MKX_UINTEGER(KaxBlockAddID) +}; + +DECLARE_MKX_BINARY(KaxCodecState) +}; + +END_LIBMATROSKA_NAMESPACE + +#endif // LIBMATROSKA_BLOCK_H diff --git a/src/KaxBlock.cpp b/src/KaxBlock.cpp index be00985..ec8cc3f 100644 --- a/src/KaxBlock.cpp +++ b/src/KaxBlock.cpp @@ -1,1072 +1,1076 @@ -/**************************************************************************** -** libmatroska : parse Matroska files, see http://www.matroska.org/ -** -** -** -** 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 - \author Julien Coloos -*/ -#include - -//#include - -#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::const_iterator Itr = ElementToClone.myBuffers.begin(); - std::vector::iterator myItr = myBuffers.begin(); - while (Itr != ElementToClone.myBuffers.end()) - { - *myItr = (*Itr)->Clone(); - Itr++; myItr++; - } -} - - -KaxBlockGroup::~KaxBlockGroup() -{ -//NOTE("KaxBlockGroup::~KaxBlockGroup"); -} - -KaxBlockGroup::KaxBlockGroup(EBML_EXTRA_DEF) - :EbmlMaster(EBML_CLASS_SEMCONTEXT(KaxBlockGroup) EBML_DEF_SEP EBML_EXTRA_CALL) - ,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; iSize() + (myBuffers[i]->Size() / 0xFF + 1)); - } - break; - case LACING_EBML: - SetSize_(GetSize() + myBuffers[0]->Size() + CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize())); - for (i=1; iSize() + CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size()), 0)); - } - break; - case LACING_FIXED: - for (i=0; iSize()); - } - 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(EBML_EXTRA_DEF) -:EBML_DEF_BINARY(KaxBlockVirtual)EBML_DEF_SEP 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) { - assert(GetSize() >= 4); - *cursor++ = TrackNumber | 0x80; // set the first bit to 1 - } else { - assert(GetSize() >= 5); - *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; iSize(); - 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; iSize()) - 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; iBuffer(), 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> 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(*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(*this); - assert(ParentCluster != NULL); - theBlock.SetParent(*ParentCluster); - ParentTrack = &track; - bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); - - KaxReferenceBlock & thePastRef = GetChild(*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(*this); - assert(ParentCluster != NULL); - theBlock.SetParent(*ParentCluster); - ParentTrack = &track; - bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); - - KaxReferenceBlock & thePastRef = GetChild(*this); - thePastRef.SetReferencedBlock(PastBlock); - thePastRef.SetParentBlock(*this); - - KaxReferenceBlock & theFutureRef = AddNewChild(*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(*this); - assert(ParentCluster != NULL); - theBlock.SetParent(*ParentCluster); - ParentTrack = &track; - bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); - - if (PastBlock != NULL) - { - KaxReferenceBlock & thePastRef = GetChild(*this); - thePastRef.SetReferencedBlock(PastBlock); - thePastRef.SetParentBlock(*this); - } - - if (ForwBlock != NULL) - { - KaxReferenceBlock & theFutureRef = AddNewChild(*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(this->FindElt(EBML_INFO(KaxBlock))); - return MyBlock.GlobalTimecode(); - -} - -uint16 KaxBlockGroup::TrackNumber() const -{ - KaxInternalBlock & MyBlock = *static_cast(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(FindFirstElt(EBML_INFO(KaxReferenceBlock))); - if (MyBlockAdds != NULL) { - Result++; - while ((MyBlockAdds = static_cast(FindNextElt(*MyBlockAdds))) != NULL) - { - Result++; - } - } - return Result; -} - -const KaxReferenceBlock & KaxBlockGroup::Reference(unsigned int Index) const -{ - KaxReferenceBlock * MyBlockAdds = static_cast(FindFirstElt(EBML_INFO(KaxReferenceBlock))); - assert(MyBlockAdds != NULL); // call of a non existing reference - - while (Index != 0) { - MyBlockAdds = static_cast(FindNextElt(*MyBlockAdds)); - assert(MyBlockAdds != NULL); - Index--; - } - return *MyBlockAdds; -} - -void KaxBlockGroup::ReleaseFrames() -{ - KaxInternalBlock & MyBlock = *static_cast(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(FindFirstElt(EBML_INFO(KaxBlockDuration), true)); - *(static_cast(&myDuration)) = TimeLength / uint64(scale); -} - -bool KaxBlockGroup::GetBlockDuration(uint64 &TheTimecode) const -{ - KaxBlockDuration * myDuration = static_cast(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(*this); - return theBlock; -} - -void KaxBlockGroup::SetParent(KaxCluster & aParentCluster) { - ParentCluster = &aParentCluster; - KaxBlock & theBlock = GetChild(*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 +/**************************************************************************** +** libmatroska : parse Matroska files, see http://www.matroska.org/ +** +** +** +** 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 + \author Julien Coloos +*/ +#include + +//#include + +#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::const_iterator Itr = ElementToClone.myBuffers.begin(); + std::vector::iterator myItr = myBuffers.begin(); + while (Itr != ElementToClone.myBuffers.end()) + { + *myItr = (*Itr)->Clone(); + Itr++; myItr++; + } +} + + +KaxBlockGroup::~KaxBlockGroup() +{ +//NOTE("KaxBlockGroup::~KaxBlockGroup"); +} + +KaxBlockGroup::KaxBlockGroup(EBML_EXTRA_DEF) + :EbmlMaster(EBML_CLASS_SEMCONTEXT(KaxBlockGroup) EBML_DEF_SEP EBML_EXTRA_CALL) + ,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; iSize() + (myBuffers[i]->Size() / 0xFF + 1)); + } + break; + case LACING_EBML: + SetSize_(GetSize() + myBuffers[0]->Size() + CodedSizeLength(myBuffers[0]->Size(), 0, IsFiniteSize())); + for (i=1; iSize() + CodedSizeLengthSigned(int64(myBuffers[i]->Size()) - int64(myBuffers[i-1]->Size()), 0)); + } + break; + case LACING_FIXED: + for (i=0; iSize()); + } + 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(EBML_EXTRA_DEF) +:EBML_DEF_BINARY(KaxBlockVirtual)EBML_DEF_SEP 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) { + assert(GetSize() >= 4); + *cursor++ = TrackNumber | 0x80; // set the first bit to 1 + } else { + assert(GetSize() >= 5); + *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; iSize(); + 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; iSize()) - 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; iBuffer(), 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> 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(*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(*this); + assert(ParentCluster != NULL); + theBlock.SetParent(*ParentCluster); + ParentTrack = &track; + bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); + + KaxReferenceBlock & thePastRef = GetChild(*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(*this); + assert(ParentCluster != NULL); + theBlock.SetParent(*ParentCluster); + ParentTrack = &track; + bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); + + KaxReferenceBlock & thePastRef = GetChild(*this); + thePastRef.SetReferencedBlock(PastBlock); + thePastRef.SetParentBlock(*this); + + KaxReferenceBlock & theFutureRef = AddNewChild(*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(*this); + assert(ParentCluster != NULL); + theBlock.SetParent(*ParentCluster); + ParentTrack = &track; + bool bRes = theBlock.AddFrame(track, timecode, buffer, lacing); + + if (PastBlock != NULL) + { + KaxReferenceBlock & thePastRef = GetChild(*this); + thePastRef.SetReferencedBlock(PastBlock); + thePastRef.SetParentBlock(*this); + } + + if (ForwBlock != NULL) + { + KaxReferenceBlock & theFutureRef = AddNewChild(*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(this->FindElt(EBML_INFO(KaxBlock))); + return MyBlock.GlobalTimecode(); + +} + +uint16 KaxBlockGroup::TrackNumber() const +{ + KaxInternalBlock & MyBlock = *static_cast(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(FindFirstElt(EBML_INFO(KaxReferenceBlock))); + if (MyBlockAdds != NULL) { + Result++; + while ((MyBlockAdds = static_cast(FindNextElt(*MyBlockAdds))) != NULL) + { + Result++; + } + } + return Result; +} + +const KaxReferenceBlock & KaxBlockGroup::Reference(unsigned int Index) const +{ + KaxReferenceBlock * MyBlockAdds = static_cast(FindFirstElt(EBML_INFO(KaxReferenceBlock))); + assert(MyBlockAdds != NULL); // call of a non existing reference + + while (Index != 0) { + MyBlockAdds = static_cast(FindNextElt(*MyBlockAdds)); + assert(MyBlockAdds != NULL); + Index--; + } + return *MyBlockAdds; +} + +void KaxBlockGroup::ReleaseFrames() +{ + KaxInternalBlock & MyBlock = *static_cast(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(FindFirstElt(EBML_INFO(KaxBlockDuration), true)); + *(static_cast(&myDuration)) = TimeLength / uint64(scale); +} + +bool KaxBlockGroup::GetBlockDuration(uint64 &TheTimecode) const +{ + KaxBlockDuration * myDuration = static_cast(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(*this); + return theBlock; +} + +void KaxBlockGroup::SetParent(KaxCluster & aParentCluster) { + ParentCluster = &aParentCluster; + KaxBlock & theBlock = GetChild(*this); + theBlock.SetParent( aParentCluster ); +} + +void KaxSimpleBlock::SetParent(KaxCluster & aParentCluster) { + KaxInternalBlock::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