Re: about deleting KaxBlockBlob objects

"Kao" <[email protected]> Thu, 24 Nov 2011 19:57:01 +0800
Newsgroups gmane.comp.multimedia.matroska.user
Message-ID <D69DED1DFB3C4830BCE30412FB8E09C7@rdminglinkao>
1. It would trigger an segmentation fault when deleting the pointer of a 
KaxBlockBlob object.
(line 83 of the attached program)

2. In KaxCluster::Render() method, all KaxBlockBlob::Block objects are 
pushed into a KaxCluster object.
Then those objects would be pointed by both KaxCluster and KaxBlockBlob.

3. How to include the V2 #defines?  Thank you!

Best regards,

Ming-Lin Kao

----- Original Message ----- 
Sent: Wednesday, November 23, 2011 7:05 PM
Subject: Re: [Matroska-users] about deleting KaxBlockBlob objects


> Hey,
>
> 1. You seem to have reversed the direction in the same; the "source"
> file is the directory named "new". The other way around would be
> correct.
>
> 2. Checking for NULL before "delete pointer;" is not needed. That's in
> the C++ standard. The patch for this part of the file doesn't change
> the functionality at all.
>
> 3. I'm not convinced that your change is actually necessary. I use
> libmatroska as it is in mkvmerge including the V2 #defines, and
> mkvmerge doesn't have memory leaks that could be traced back to this
> part of libmatroska. Please show me a test case.
>
> Kind regards,
> mosu
> _______________________________________________
> Matroska-users mailing list
> [email protected]
> http://lists.matroska.org/cgi-bin/mailman/listinfo/matroska-users
> Read Matroska-Users on GMane: 
> http://dir.gmane.org/gmane.comp.multimedia.matroska.user

_______________________________________________
Matroska-users mailing list
[email protected]
http://lists.matroska.org/cgi-bin/mailman/listinfo/matroska-users
Read Matroska-Users on GMane: http://dir.gmane.org/gmane.comp.multimedia.matroska.user
test7.cpp (text/plain, 13.1 KB)
/****************************************************************************
** libmatroska : parse Matroska files, see http://www.matroska.org/
**
** <file/class description>
**
** Copyright (C) 2002-2004 Steve Lhomme.  All rights reserved.
**
** This file is part of libmatroska.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
** 
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
** 
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**
** See http://www.matroska.org/license/lgpl/ for LGPL licensing information.**
** Contact [email protected] if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

/*!
    \file
    \version \$Id: test6.cpp 725 2011-03-27 17:09:02Z robux4 $
    \brief Test muxing two tracks into valid clusters/blocks/frames
    \author Steve Lhomme     <robux4 @ users.sf.net>
*/

#ifdef _MSC_VER
#include <windows.h>      // for min/max
#endif // _MSC_VER

#include <iostream>
#include <vector>

#include "ebml/StdIOCallback.h"

#include "ebml/EbmlHead.h"
#include "ebml/EbmlSubHead.h"
#include "ebml/EbmlVoid.h"
#include "matroska/FileKax.h"
#include "matroska/KaxSegment.h"
#include "matroska/KaxTracks.h"
#include "matroska/KaxTrackEntryData.h"
#include "matroska/KaxTrackVideo.h"
#include "matroska/KaxCluster.h"
#include "matroska/KaxClusterData.h"
#include "matroska/KaxSeekHead.h"
#include "matroska/KaxCues.h"
#include "matroska/KaxInfo.h"
#include "matroska/KaxInfoData.h"
#include "matroska/KaxVersion.h"

using namespace LIBMATROSKA_NAMESPACE;
using namespace std;

static const char *OUTPUT_FILE_NAME   = "test1.mkv";
static const bool bWriteDefaultValues = false;
static const uint64 TIMECODE_SCALE = 1000000ull;

static std::vector<KaxBlockBlob *> usedBlobs;

static KaxBlockBlob *AllocateKaxBlockBlob(BlockBlobType type)
{
	KaxBlockBlob *result = new KaxBlockBlob(type);
	usedBlobs.push_back(result);
	return result;
}

static void RecycleAllKaxBlockBlobs()
{
	std::vector<KaxBlockBlob *>::iterator it;
	for (it = usedBlobs.begin(); it < usedBlobs.end(); it++)
	{
		delete *it;
	}
	usedBlobs.clear();
}

int main(int argc, char **argv)
{
	uint64 fakeTimecode = time(NULL) * 1000000000ull;
	size_t fakeDataSize = 100;
	unsigned char fakeData[fakeDataSize];
	memset(fakeData, 0xAA, fakeDataSize);

	IOCallback    *pMKVFile = NULL;
	EbmlHead      *pMKVHead = NULL;
	KaxSegment    *pSegment = NULL;
	filepos_t      segmentSize = 0;

	KaxSeekHead   *pMetaSeek = NULL;
	EbmlVoid      *pMetaSeekDummy = NULL;
	KaxCues       *pAllCues = NULL;
	EbmlVoid      *pAllCuesDummy = NULL;

	KaxTrackEntry *pVideoTrack = NULL;
	KaxTrackEntry *pSubtitleTrack = NULL;

	bool           isFirstCluster;
	KaxCluster    *pCluster;
	KaxBlockBlob  *pBlockBlob;
	KaxBlockBlob  *pLastVideoBlockBlob;
	uint64         clusterMinTimecode;
	uint64         firstFrameTimecode;
	uint64         lastFrameTimecode;

	KaxBlockGroup *pLastSubtitleBlockGroup;
	uint64         lastSubtitleTimecode;
	uint64         lastSubtitlePosition;
	unsigned char *pSubtitleData;
	size_t         subtitleDataSize;

	try
	{
		// write the head of the file
		pMKVFile = new StdIOCallback(OUTPUT_FILE_NAME, MODE_CREATE);

		// Writing EBML test
		pMKVHead = new EbmlHead();
		*static_cast<EbmlString *>(&GetChild<EDocType>(*pMKVHead)) = "matroska";
		*static_cast<EbmlUInteger *>(&GetChild<EDocTypeVersion>(*pMKVHead)) = MATROSKA_VERSION;
		*static_cast<EbmlUInteger *>(&GetChild<EDocTypeReadVersion>(*pMKVHead)) = MATROSKA_VERSION;
		pMKVHead->Render(*pMKVFile, true);

		pSegment = new KaxSegment();

		// size is unknown and will always be, we can render it right away
		// 5 octets can represent 2^35, it is wide enough for almost any file
		segmentSize = pSegment->WriteHead(*pMKVFile, 5, bWriteDefaultValues);

		// reserve some space for the Meta Seek writen at the end
		pMetaSeek = new KaxSeekHead();
		pMetaSeekDummy = new EbmlVoid();
		pMetaSeekDummy->SetSize(300);
		segmentSize += pMetaSeekDummy->Render(*pMKVFile, bWriteDefaultValues);

		// fill the mandatory Info section
		KaxInfo &segmentInfo = GetChild<KaxInfo>(*pSegment);

		*static_cast<EbmlUInteger *>(&GetChild<KaxTimecodeScale>(segmentInfo)) = TIMECODE_SCALE;
		*static_cast<EbmlFloat *>(&GetChild<KaxDuration>(segmentInfo)) = 0.0;  // fix it later

		std::string muxingAppString = "libebml v";
		muxingAppString += EbmlCodeVersion.c_str();
		muxingAppString += " + libmatroska v";
		muxingAppString += KaxCodeVersion.c_str();
		UTFstring muxingAppUTFstring;
		muxingAppUTFstring.SetUTF8(muxingAppString);
		*(EbmlUnicodeString *)&GetChild<KaxMuxingApp>(segmentInfo)  = muxingAppUTFstring;
		*(EbmlUnicodeString *)&GetChild<KaxWritingApp>(segmentInfo) = L"muxer";

		GetChild<KaxDateUTC>(segmentInfo).SetEpochDate(time(NULL));  // FIXME: use the timecode of 1st frame

		unsigned char segmentUIDBuffer[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
		GetChild<KaxSegmentUID>(segmentInfo).CopyBuffer(segmentUIDBuffer, 16);

		segmentSize += segmentInfo.Render(*pMKVFile, true);
		pMetaSeek->IndexThis(segmentInfo, *pSegment);

		KaxTracks &AllTracks = GetChild<KaxTracks>(*pSegment);
		pVideoTrack = &GetChild<KaxTrackEntry>(AllTracks);
		pSubtitleTrack = &GetNextChild<KaxTrackEntry>(AllTracks, *pVideoTrack);

		// fill video track params
		{
			pVideoTrack->SetGlobalTimecodeScale(TIMECODE_SCALE);

			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackNumber>(*pVideoTrack)) = 1;
			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackUID>(*pVideoTrack)) = 1;
			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackType>(*pVideoTrack)) = track_video;

			*static_cast<EbmlString *>(&GetChild<KaxTrackLanguage>(*pVideoTrack)) = "eng";

			*static_cast<EbmlString *>(&GetChild<KaxCodecID>(*pVideoTrack)) = "V_MPEG4/ISO/ASP";

			pVideoTrack->EnableLacing(false);

			// video specific params
			KaxTrackVideo &videoTrackParameters = GetChild<KaxTrackVideo>(*pVideoTrack);
			*static_cast<EbmlUInteger *>(&GetChild<KaxVideoPixelWidth>(videoTrackParameters)) = 640;
			*static_cast<EbmlUInteger *>(&GetChild<KaxVideoPixelHeight>(videoTrackParameters)) = 480;
		}

		// fill subtitle track params
		{
			pSubtitleTrack->SetGlobalTimecodeScale(TIMECODE_SCALE);

			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackNumber>(*pSubtitleTrack)) = 3;
			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackUID>(*pSubtitleTrack)) = 3;
			*static_cast<EbmlUInteger *>(&GetChild<KaxTrackType>(*pSubtitleTrack)) = track_subtitle;

			*static_cast<EbmlString *>(&GetChild<KaxTrackLanguage>(*pSubtitleTrack)) = "eng";

			*static_cast<EbmlString *>(&GetChild<KaxCodecID>(*pSubtitleTrack)) = "S_TEXT/UTF8";

			pSubtitleTrack->EnableLacing(false);
		}

		segmentSize += AllTracks.Render(*pMKVFile, bWriteDefaultValues);
		pMetaSeek->IndexThis(AllTracks, *pSegment);

		pAllCues = new KaxCues();
		pAllCues->SetGlobalTimecodeScale(TIMECODE_SCALE);
		pAllCuesDummy = new EbmlVoid();
		pAllCuesDummy->SetSize(400);
		segmentSize += pAllCuesDummy->Render(*pMKVFile, bWriteDefaultValues);
	}
	catch (...)
	{
		// some error
		printf("ERROR: fail to startup\n");
		if (pMKVFile != NULL)
		{
			pMKVFile->close();
		}
		goto end;
	}

	isFirstCluster = true;
	pCluster = NULL;
	pBlockBlob = NULL, pLastVideoBlockBlob = NULL;
	clusterMinTimecode = 0ull;
	firstFrameTimecode = 0ull;
	lastFrameTimecode = 0ull;
	pLastSubtitleBlockGroup = NULL;
	lastSubtitlePosition = 0ull;
	pSubtitleData = NULL;
	subtitleDataSize = 0;

	try
	{
		for (int i = 0; i < 5; i++)
		{
			if (firstFrameTimecode == 0ull)
			{
				firstFrameTimecode = fakeTimecode;
			}

			// release the last cluster
			if (pCluster != NULL)
			{
				segmentSize += pCluster->Render(*pMKVFile, *pAllCues, bWriteDefaultValues);

				if (pLastSubtitleBlockGroup != NULL)
				{
					lastSubtitleTimecode = pLastSubtitleBlockGroup->GlobalTimecode();
					lastSubtitlePosition = GetChild<KaxBlockDuration>(*pLastSubtitleBlockGroup).GetElementPosition();
					pLastSubtitleBlockGroup = NULL;
				}

				if (isFirstCluster)
				{
					isFirstCluster = false;
					pMetaSeek->IndexThis(*pCluster, *pSegment);
				}

				pCluster->ReleaseFrames();
				delete pCluster;
				pCluster = NULL;
				RecycleAllKaxBlockBlobs();
			}

			// allocate a new cluster
			pCluster = new KaxCluster();
			pCluster->SetParent(*pSegment); // mandatory to store references in this Cluster
			pCluster->InitTimecode(fakeTimecode / TIMECODE_SCALE, TIMECODE_SCALE);

			pLastVideoBlockBlob = NULL;
			clusterMinTimecode = fakeTimecode;

			for (int j = 0; j < 30; j++, fakeTimecode += 33000000ull)
			{
				// prepare a frame
				DataBuffer *pMyData = new DataBuffer((binary *)fakeData, fakeDataSize);

				KaxTrackEntry *pTrack = j == 0 ? pSubtitleTrack : pVideoTrack;

				// allocate current blob
				pBlockBlob = AllocateKaxBlockBlob((pTrack == pSubtitleTrack) ? BLOCK_BLOB_NO_SIMPLE : BLOCK_BLOB_ALWAYS_SIMPLE);
				pCluster->AddBlockBlob(pBlockBlob);
				pBlockBlob->SetParent(*pCluster);

				// add a frame
				pBlockBlob->AddFrameAuto(*pTrack, fakeTimecode, *pMyData, LACING_AUTO, j > 1 ? pLastVideoBlockBlob : NULL);
				if (pTrack == pSubtitleTrack)
				{
					if (pLastSubtitleBlockGroup != NULL)
					{
						// update the timecode of the last subtitle frame
						pLastSubtitleBlockGroup->SetBlockDuration(fakeTimecode - pLastSubtitleBlockGroup->GlobalTimecode());
					}

					pBlockBlob->SetBlockDuration(1000 * 1000000000ull);  // temporarily be the maximal value
					GetChild<KaxBlockDuration>((KaxBlockGroup &)*pBlockBlob).SetDefaultSize(8);  // with a fixed size
					pLastSubtitleBlockGroup = &(KaxBlockGroup &)*pBlockBlob;

					if (lastSubtitlePosition != 0ull)
					{
						// re-calculate subtitleBlockDuration
						KaxBlockDuration subtitleBlockDuration;
						*static_cast<EbmlUInteger *>(&subtitleBlockDuration) = (fakeTimecode - lastSubtitleTimecode) / TIMECODE_SCALE;
						subtitleBlockDuration.SetDefaultSize(8);

						// correct subtitleBlockDuration
						uint64 currentPosition = pMKVFile->getFilePointer();
						pMKVFile->setFilePointer(lastSubtitlePosition);
						subtitleBlockDuration.Render(*pMKVFile, false, true, true);
						pMKVFile->setFilePointer(currentPosition);

						lastSubtitlePosition = 0ull;
					}
				}

				// add a cue for any new cluster
				if ((pTrack == pVideoTrack) && (pLastVideoBlockBlob == NULL))
				{
					pAllCues->AddBlockBlob(*pBlockBlob);
				}

				if (pTrack == pVideoTrack)
				{
					pLastVideoBlockBlob = pBlockBlob;
				}

				lastFrameTimecode = fakeTimecode;
			} // end for
		} // end for
	}
	catch (...)
	{
		// some error
		printf("ERROR: fail to append a frame\n");
	}

	if (pCluster != NULL)
	{
		try
		{
			// release the last cluster
			segmentSize += pCluster->Render(*pMKVFile, *pAllCues, bWriteDefaultValues);
			pCluster->ReleaseFrames();
			delete pCluster;
			pCluster = NULL;
			RecycleAllKaxBlockBlobs();
		}
		catch (...)
		{
			// some error
			printf("ERROR: fail to store and release a cluster\n");
		}

		try
		{
			// re-calculate segDuration & subtitleBlockDuration
			KaxDuration &segDuration = GetChild<KaxDuration>(GetChild<KaxInfo>(*pSegment));
			*static_cast<EbmlFloat *>(&segDuration) = (lastFrameTimecode - firstFrameTimecode) / TIMECODE_SCALE;
			KaxBlockDuration subtitleBlockDuration;
			if (lastSubtitlePosition != 0ull)
			{
				*static_cast<EbmlUInteger *>(&subtitleBlockDuration) = (lastFrameTimecode - lastSubtitleTimecode) / TIMECODE_SCALE;
				subtitleBlockDuration.SetDefaultSize(8);
			}

			// correct segDuration & subtitleBlockDuration
			uint64 currentPosition = pMKVFile->getFilePointer();
			pMKVFile->setFilePointer(segDuration.GetElementPosition());
			segDuration.Render(*pMKVFile, false, true, true);
			if (lastSubtitlePosition != 0ull)
			{
				pMKVFile->setFilePointer(lastSubtitlePosition);
				subtitleBlockDuration.Render(*pMKVFile, false, true, true);
				lastSubtitlePosition = 0ull;
			}
			pMKVFile->setFilePointer(currentPosition);

			pAllCuesDummy->ReplaceWith(*pAllCues, *pMKVFile, bWriteDefaultValues);
			pMetaSeek->IndexThis(*pAllCues, *pSegment);

			pMetaSeekDummy->ReplaceWith(*pMetaSeek, *pMKVFile, bWriteDefaultValues);

			// let's assume we know the size of the Segment element
			// the size of the pSegment is also computed because mandatory elements we don't write ourself exist
			if (pSegment->ForceSize(segmentSize - pSegment->HeadSize()))
			{
				pSegment->OverwriteHead(*pMKVFile);
			}
		}
		catch (...)
		{
			// some error
			printf("ERROR: fail to close the file\n");
		}

		pMKVFile->close();
	}
	else
	{
		// error: empty file
		pMKVFile->close();
		try
		{
			remove(OUTPUT_FILE_NAME);
		}
		catch (...)
		{
			// some error: don't care
		}
	}

end:
	delete pSubtitleData;
	delete pCluster;
	delete pAllCuesDummy;
	delete pAllCues;
	delete pMetaSeekDummy;
	delete pMetaSeek;
	delete pSegment;
	delete pMKVHead;
	delete pMKVFile;

    return 0;
}