view IM_AF Decoder/IM_AF_Decoder.c @ 0:138a3cea9792

Different files related to my project, including the encoder (I made it) and decoder. Also I added the mp4atoms as an example. MP4atoms belong to the IMAF player library.
author Eugenio Oñate Hospital <eo301@eecs.qmul.ac.uk>
date Wed, 04 Jul 2012 22:16:23 +0100
parents
children
line wrap: on
line source
#include "MP4Movies.h"
#include "MP4LinkedList.h"
#include "MP4InputStream.h"
#include "IM_AF_Decoder.h"

#ifndef BAILWITHERROR
#define BAILWITHERROR(v) \
	{ \
	err = (v); \
	goto bail; \
	}
#endif

typedef struct IMAFDecoder
{
	u8 *InputMafFilename;
	FILE *InputFp;
	u32 TrackCount;
//	u32 CompletedTrackCount;
	u32 HasTimedTextTrack;

	MP4Movie moov;
	MP4Track trak;
	MP4Media media;
	u32 MovieTimescale;
	u32 MovieDuration;

	// Multi audio track
	MP4LinkedList TrackReaderList;	// for all Track
	MP4LinkedList TrackSampleList;	// for all Track Sample

	MP4TrackReader TimedTextTrackReader;
	MP4Handle TimedTextSampleH;
	MP4Handle TextH;
	u32 TimedTextTrackIndex;
	TextSampleStyleParam *TextParam;

	unsigned int HasSaocTrack;
}IMAFDecoder;

void InitTextSampleStyleParam(TextSampleStyleParam *TextParam)
{
	if(TextParam)
	{
		if(TextParam->styl_text_styles)
			free(TextParam->styl_text_styles);
		if(TextParam->krok_highlight_end_time)
			free(TextParam->krok_highlight_end_time);
		if(TextParam->krok_startcharoffset)
			free(TextParam->krok_startcharoffset);
		if(TextParam->krok_endcharoffset)
			free(TextParam->krok_endcharoffset);
		if(TextParam->font)
			free(TextParam->font);
		memset(TextParam, 0, sizeof(TextSampleStyleParam));
	}
}
	
IMAF_DECODER_API IMAF_Err IMAF_Decoder_Create(IMAF_DecoderH *IMafDecoder, IMAF_Decoder_Param *param)
{
	IMAFDecoder *decoder = NULL;
	MP4Err err;
	u32 i;
	u32 sample_desc_type;
	MP4TrackReader reader;
	MP4Handle sampleH;
	u32 minorVersion;

	decoder = calloc(1, sizeof(IMAFDecoder));

	if(param == NULL || param->InputMafFilename == NULL)
	{
		err = MP4BadParamErr;
		goto bail;
	}
	else
		decoder->InputMafFilename = _strdup(param->InputMafFilename);

	err = MP4OpenMovieFile( &decoder->moov, decoder->InputMafFilename, MP4OpenMovieInPlace ); if (err) goto bail;
	err = MP4GetMovieTrackCount( decoder->moov, &decoder->TrackCount); if(err) goto bail;

	//Timed Text
	for(i=0; i<decoder->TrackCount; i++)
	{
		err = MP4GetMovieIndTrack( decoder->moov, i+1, &decoder->trak ); if (err) goto bail;
		err = MP4GetTrackMedia( decoder->trak, &decoder->media ); if (err) goto bail;
		err = MP4GetMediaSampleDescriptionType(decoder->media, &sample_desc_type); if(err) goto bail;
		if(sample_desc_type == 'tx3g')
		{
			decoder->HasTimedTextTrack = 1;
			decoder->TimedTextTrackIndex = i;
			break;
		}
	}

	param->HasTimedTextTrack = decoder->HasTimedTextTrack;
	param->NumberOfAudioTrack = decoder->TrackCount - decoder->HasTimedTextTrack;

	err = MP4GetMovieTimeScale(decoder->moov, &decoder->MovieTimescale); if(err) goto bail;
	param->MovieTimescale = decoder->MovieTimescale;

	err = MP4GetMovieDuration(decoder->moov, (u64*)&decoder->MovieDuration); if(err) goto bail;
	param->MovieDuration = decoder->MovieDuration;

	// Create TrackReader for all Track
	err = MP4MakeLinkedList( &decoder->TrackReaderList); if(err) goto bail;
	err = MP4MakeLinkedList( &decoder->TrackSampleList); if(err) goto bail;
	for(i=0; i<decoder->TrackCount; i++)
	{
		err = MP4GetMovieIndTrack( decoder->moov, i+1, &decoder->trak ); if (err) goto bail;
		if(decoder->HasTimedTextTrack && decoder->TimedTextTrackIndex == i)
		{
			err = MP4CreateTrackReader( decoder->trak, &reader ); if (err) goto bail;
			decoder->TimedTextTrackReader = reader;
		}
		else
		{
			err = MP4CreateTrackReader( decoder->trak, &reader ); if (err) goto bail;
			err = MP4AddListEntry( reader, decoder->TrackReaderList); if (err) goto bail;

			err = MP4NewHandle(0, &sampleH); if(err) goto bail;
			err = MP4AddListEntry( sampleH, decoder->TrackSampleList); if (err) goto bail;
		}
	}

	err = MP4NewHandle(0, &decoder->TimedTextSampleH); if(err) goto bail;
	err = MP4NewHandle(0, &decoder->TextH); if(err) goto bail;

	decoder->TextParam = calloc(1, sizeof(TextSampleStyleParam));

	 err = ISOGetMovieBrand(decoder->moov, &param->MajorBrand, &minorVersion); if(err) goto bail;
	 err = ISOGetNbCompatableBrands(decoder->moov, &param->NumberOfCompatibleBrands); if(err) goto bail;
	 if(param->NumberOfCompatibleBrands)
	 {
		 param->CompatibleBrands = calloc(param->NumberOfCompatibleBrands, sizeof(u32));
		err = ISOGetCompatableBrands(decoder->moov, param->CompatibleBrands); if(err) goto bail;
	 }
	 else
		 param->CompatibleBrands = NULL;


	*IMafDecoder = decoder;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetAudioTrackInfos(IMAF_DecoderH IMafDecoder, unsigned int *AudioTrackObjectTypeArray
														  ,unsigned int *AudioTrackIdArray, IMAF_AudioParam *AudioParamArray)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	u32 i;
	u32 outObjectType, sample_desc_type;
	u32 hasTimedText = 0;
	u32 trackID;
	unsigned int nBitsPerSample;
	unsigned int nChannels;
	unsigned int nSamplingFrequency;
	for(i=0; i<decoder->TrackCount - decoder->HasTimedTextTrack; i++)
	{
		err = MP4GetMovieIndTrack( decoder->moov, i+1, &decoder->trak ); if (err) goto bail;
		err = MP4GetTrackMedia( decoder->trak, &decoder->media ); if (err) goto bail;
		err = MP4GetMediaSampleDescriptionType(decoder->media, &sample_desc_type); if(err) goto bail;
		if(sample_desc_type == 'mp4a')
		{
			err = MP4GetTrackID(decoder->trak, &trackID); if(err) goto bail;
			AudioTrackIdArray[i - hasTimedText] = trackID;
			err = MP4GetMediaDecoderType( decoder->media, 1, &outObjectType, NULL, NULL, NULL ); if(err) goto bail;
			if(outObjectType == AUDIO_OBJECT_TYPE_INDICATION_MP3 || outObjectType == AUDIO_OBJECT_TYPE_INDICATION_AAC
				|| outObjectType == AUDIO_OBJECT_TYPE_INDICATION_SAOC|| outObjectType == AUDIO_OBJECT_TYPE_INDICATION_PCM)
			{
				AudioTrackObjectTypeArray[i - hasTimedText] = outObjectType;
				if(outObjectType = AUDIO_OBJECT_TYPE_INDICATION_SAOC)
					decoder->HasSaocTrack = 1;
			}
			else
				BAILWITHERROR(MP4BadDataErr)

			err = MP4GetSampleRateChannelBitsPerSample(decoder->media, &nSamplingFrequency, &nChannels, &nBitsPerSample); if(err) goto bail;
			AudioParamArray[i].nSamplingFrequency = nSamplingFrequency;
			AudioParamArray[i].nChannels = nChannels;
			AudioParamArray[i].nBitsPerSample = nBitsPerSample;
		}
		else
		{
			hasTimedText = 1;
		}
	}

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetGroupContainerInfo(IMAF_DecoderH IMafDecoder, unsigned int *GroupCount)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	err = MP4GetGroupContainerInfo(decoder->moov, GroupCount); if(err) goto bail;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetGroupByIndex(IMAF_DecoderH IMafDecoder, unsigned int i, IMAF_Group *grup)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	unsigned int *element_ID;
	char *group_name;
	char *group_description;

	err = MP4GetGroupByIndex(decoder->moov, i, 
		&grup->group_ID,
		&grup->num_elements,
		&element_ID,
		&grup->group_activation_mode,
		&grup->group_activation_elements_number,
		&grup->group_reference_volume,
		&group_name,
		&group_description); if(err) goto bail;

	grup->element_ID =(u32*) calloc( grup->num_elements, sizeof(u32) );
	memcpy(grup->element_ID, element_ID, sizeof(u32)*grup->num_elements);
	if(group_name)
		grup->group_name = _strdup(group_name);
	else
		grup->group_name = NULL;
	if(group_description)
		grup->group_description = _strdup(group_description);
	else
		grup->group_description = NULL;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetPresetContainerInfo(IMAF_DecoderH IMafDecoder, unsigned int *num_preset, unsigned int *default_preset_ID)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	err = MP4GetPresetContainerInfo(decoder->moov, num_preset, default_preset_ID); if(err) goto bail;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetPresetByIndex(IMAF_DecoderH IMafDecoder, unsigned int i, IMAF_Preset *prst)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	u32 *preset_element_ID;
	u32 *preset_volume_element;
	u32 *num_input_channel;
	u32 *updated_sample_number;
	char *preset_name;
	u32 ii;
	u32 element_count;

	err = MP4GetPresetByIndex(decoder->moov, i,
						&prst->preset_ID,
						&prst->num_preset_elements,
						&preset_element_ID,	
						&prst->preset_type,           
						&prst->preset_global_volume,
						&preset_volume_element,
						&num_input_channel ,
						&prst->output_channel_type ,
						&prst->num_output_channel ,
						&prst->num_updates ,
						&updated_sample_number,
						&preset_name); if(err) goto bail;

	prst->preset_element_ID =(u32*) calloc( prst->num_preset_elements, sizeof(u32) );
	memcpy(prst->preset_element_ID, preset_element_ID, sizeof(u32)*prst->num_preset_elements);
	if(prst->preset_type == 0)
	{
		element_count = prst->num_preset_elements;
		prst->preset_volume_element =(u32*) calloc( element_count, sizeof(u32) );
		for(ii=0; ii<element_count; ii++)
			prst->preset_volume_element[ii] = preset_volume_element[ii]*2;
	}
	else if(prst->preset_type == 1)
	{
		element_count = prst->num_preset_elements*num_input_channel[0]*prst->num_output_channel;
		prst->preset_volume_element =(u32*) calloc( element_count, sizeof(u32) );
		for(ii=0; ii<element_count; ii++)
			prst->preset_volume_element[ii] = preset_volume_element[ii]*2;
	}
	else if(prst->preset_type == 2)
	{
		element_count = prst->num_preset_elements * prst->num_updates;
		prst->preset_volume_element =(u32*) calloc( element_count, sizeof(u32) );
		for(ii=0; ii<element_count; ii++)
			prst->preset_volume_element[ii] = preset_volume_element[ii]*2;
	}
	else if(prst->preset_type == 3)
	{
		element_count = prst->num_updates*prst->num_preset_elements*num_input_channel[0]*prst->num_output_channel;
		prst->preset_volume_element =(u32*) calloc( element_count, sizeof(u32) );
		for(ii=0; ii<element_count; ii++)
			prst->preset_volume_element[ii] = preset_volume_element[ii]*2;
	}

	if(num_input_channel)
	{
		prst->num_input_channel =(u32*) calloc( prst->num_preset_elements, sizeof(u32) );
		memcpy(prst->num_input_channel, num_input_channel, sizeof(u32)*prst->num_preset_elements);
	}
	else
		prst->num_input_channel = NULL;
	if(updated_sample_number)
	{
		prst->updated_sample_number =(u32*) calloc( prst->num_updates, sizeof(u32) );
		memcpy(prst->updated_sample_number, updated_sample_number, sizeof(u32)*prst->num_updates);
	}
	else
		prst->updated_sample_number = NULL;
	if(preset_name)
		prst->preset_name = _strdup(preset_name);
	else
		prst->preset_name = NULL;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetRuleContainerInfo(IMAF_DecoderH IMafDecoder, unsigned int *num_selection_rules, unsigned int *num_mixing_rules)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	
	err = MP4GetRuleContainerInfo(decoder->moov, num_selection_rules, num_mixing_rules); if(err) goto bail;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetSelectionRuleByIndex(IMAF_DecoderH IMafDecoder, unsigned int i, IMAF_SelectionRule *rusc)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	char *selection_rule_description;
	err = MP4GetSelectionRuleByIndex(decoder->moov, i,
										&rusc->selection_rule_ID,
										&rusc->selection_rule_type,
										&rusc->element_ID,
										&rusc->min_num_elements,
										&rusc->max_num_elements,
										&rusc->key_element_ID,
										&selection_rule_description); if(err) goto bail;

	if(selection_rule_description)
		rusc->selection_rule_description = _strdup(selection_rule_description);
	else
		rusc->selection_rule_description = NULL;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_GetMixingRuleByIndex(IMAF_DecoderH IMafDecoder, unsigned int i, IMAF_MixingRule *rumx)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	char *mixing_rule_description;
	err = MP4GetMixingRuleByIndex(decoder->moov, i,
		&rumx->mixing_rule_ID,
		&rumx->mixing_rule_type,
		&rumx->element_ID,
		&rumx->min_volume,
		&rumx->max_volume,
		&rumx->key_element_ID,
		&mixing_rule_description); if(err) goto bail;

	if(mixing_rule_description)
		rumx->mixing_rule_description = _strdup(mixing_rule_description);
	else
		rumx->mixing_rule_description = NULL;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_SeekSample(IMAF_DecoderH IMafDecoder, unsigned int mediaTime)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	u32 timescale, i;
	MP4TrackReader reader;

	err = MP4GetMediaTimeScale(decoder->media, &timescale); if(err) goto bail;
	mediaTime /= 1000;		//in miliseconds
	mediaTime *= timescale;

	for(i=0; i<decoder->TrackCount - decoder->HasTimedTextTrack; i++)
	{
		err = MP4GetListEntry(decoder->TrackReaderList, i, (char**)&reader); if(err) goto bail;
		err = MP4TrackReaderSeekToSyncSample( reader, mediaTime); if(err) goto bail;
	}
	if(decoder->TimedTextTrackReader)
	{
		err = MP4TrackReaderSeekToSyncSample( decoder->TimedTextTrackReader, mediaTime); if(err) goto bail;
	}
	
bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_GetNextAudioSampleByIndex(IMAF_DecoderH IMafDecoder, unsigned int i,
																unsigned char **SampleData, unsigned int *SampleDataSize, unsigned int *CurrentTime, unsigned int *sampleNumber)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	MP4TrackReader reader;
	MP4Handle sampleH;
	u32 unitSize;
	u32 sampleFlags;
	u32 cts;
	u32 dts;
	
	if(i >= decoder->TrackCount - decoder->HasTimedTextTrack)
		BAILWITHERROR(MP4BadParamErr)
	err = MP4GetListEntry(decoder->TrackReaderList, i, (char**)&reader); if(err) goto bail;
	err = MP4GetListEntry(decoder->TrackSampleList, i, (char**)&sampleH); if(err) goto bail;
	err = MP4TrackReaderGetNextAccessUnitWithSampleNumber( reader, sampleH, &unitSize,&sampleFlags, &cts, &dts, sampleNumber);

	if ( err )
	{
		if ( err == MP4EOF )
		{
			*SampleData = NULL;
			*SampleDataSize = 0;
			*CurrentTime = 0;
			err = MP4NoErr;
		}
		else
			goto bail;
	}

	*CurrentTime = dts;
	*SampleData = *sampleH;
	*SampleDataSize = unitSize;
bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_GetNextTextSample(IMAF_DecoderH IMafDecoder, 
				unsigned char **SampleData, unsigned int *SampleDataSize, unsigned int *CurrentTime, TextSampleStyleParam **param)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	u32 unitSize;
	u32 sampleFlags;
	u32 cts;
	u32 dts;
	u32 i;
	u8 *TextSample;
	MP4InputStreamPtr is;
	u32 TextModifiersSize, TotalAtomSize;
	MP4AtomPtr entry;
	TextStyleAtomPtr styl;
	TextHighlightAtomPtr hlit;
	TextHighlightColorAtomPtr hclr;
	TextKaraokeAtomPtr krok;
	TextSampleEntryValue tx3g_value;
	
	
	err = MP4TrackReaderGetNextAccessUnit(decoder->TimedTextTrackReader, decoder->TimedTextSampleH, &unitSize, &sampleFlags, &cts, &dts);

	if ( err )
	{
		if ( err == MP4EOF )
		{
			*SampleData = NULL;
			*SampleDataSize = 0;
			*CurrentTime = 0;
			return MP4EOF;
		}
		else
			goto bail;
	}

	err = MP4TrackReaderGetTextSampleEntryValues(decoder->TimedTextTrackReader, &tx3g_value); if(err) goto bail;

#if 0	//only text
	*CurrentTime = dts;
	*SampleData = *decoder->TimedTextSampleH;
	*SampleDataSize = unitSize;
#else
	*CurrentTime = dts;
	TextSample = *decoder->TimedTextSampleH;
	*SampleDataSize = TextSample[0]<<8 | TextSample[1];
	TextSample+=2;
	MP4SetHandleSize(decoder->TextH, *SampleDataSize+1);
	memcpy(*decoder->TextH, TextSample, *SampleDataSize);
	TextSample += *SampleDataSize;
	(*decoder->TextH)[*SampleDataSize] = '\0';
	*SampleData = *decoder->TextH;

	//Extract Text Style
	InitTextSampleStyleParam(decoder->TextParam);
	decoder->TextParam->displayFlags=								tx3g_value.displayFlags;
	decoder->TextParam->horizontal_justification=				tx3g_value.horizontal_justification;
	decoder->TextParam->vertical_justification=					tx3g_value.vertical_justification;
	decoder->TextParam->background_color_r=					tx3g_value.background_color_r;
	decoder->TextParam->background_color_g=					tx3g_value.background_color_g;
	decoder->TextParam->background_color_b=					tx3g_value.background_color_b;
	decoder->TextParam->background_color_a=					tx3g_value.background_color_a;
	decoder->TextParam->default_text_box_top=					tx3g_value.default_text_box_top;
	decoder->TextParam->default_text_box_left=					tx3g_value.default_text_box_left;
	decoder->TextParam->default_text_box_bottom=			tx3g_value.default_text_box_bottom;
	decoder->TextParam->default_text_box_right=				tx3g_value.default_text_box_right;
	decoder->TextParam->default_style_startChar=				tx3g_value.default_style_startChar;
	decoder->TextParam->default_style_endChar=				tx3g_value.default_style_endChar;
	decoder->TextParam->default_style_font_ID=					tx3g_value.default_style_font_ID;
	decoder->TextParam->default_style_face_style_flags=  	tx3g_value.default_style_face_style_flags;
	decoder->TextParam->default_style_font_size=				tx3g_value.default_style_font_size;
	decoder->TextParam->default_style_text_color_r=			tx3g_value.default_style_text_color_r;
	decoder->TextParam->default_style_text_color_g=			tx3g_value.default_style_text_color_g;
	decoder->TextParam->default_style_text_color_b=			tx3g_value.default_style_text_color_b;
	decoder->TextParam->default_style_text_color_a=			tx3g_value.default_style_text_color_a;
	decoder->TextParam->font_ID=										tx3g_value.font_ID;
	decoder->TextParam->font_name_length=						tx3g_value.font_name_length;
	decoder->TextParam->font=												_strdup(tx3g_value.font);
	free(tx3g_value.font);

	TotalAtomSize= 0;
	TextModifiersSize = unitSize - 2 - *SampleDataSize;
	err = MP4CreateMemoryInputStream(TextSample, TextModifiersSize, &is ); if (err) goto bail;
	is->debugging = 0;
	while(TextModifiersSize>TotalAtomSize)
	{
		err = MP4ParseAtom( is, &entry ); if (err) goto bail;
		TotalAtomSize += entry->size;
		if(entry->type == 'styl')
		{
			StyleRecordStructPtr styleRecord;
			styl = (TextStyleAtomPtr)entry;
			decoder->TextParam->styl_entry_count = styl->entry_count;
			decoder->TextParam->styl_text_styles = (StyleRecord*)calloc(styl->entry_count, sizeof(styleRecord));
			for(i=0; i<styl->entry_count; i++)
			{
				err = MP4GetListEntry(styl->styleRecordList, i, (char**)&styleRecord); if(err) goto bail;
				memcpy(&decoder->TextParam->styl_text_styles[i], styleRecord, sizeof(StyleRecord));
			}
		}
		else if(entry->type == 'hlit')
		{
			hlit = (TextHighlightAtomPtr)entry;
			decoder->TextParam->hlit_startcharoffset = hlit->startcharoffset;
			decoder->TextParam->hlit_endcharoffset = hlit->endcharoffset;
		}
		else if(entry->type == 'hclr')
		{
			hclr = (TextHighlightColorAtomPtr)entry;
			decoder->TextParam->hclr_highlight_color_r = hclr->highlight_color_r;
			decoder->TextParam->hclr_highlight_color_g = hclr->highlight_color_g;
			decoder->TextParam->hclr_highlight_color_b = hclr->highlight_color_b;
			decoder->TextParam->hclr_highlight_color_a = hclr->highlight_color_a;
		}
		else if(entry->type == 'krok')
		{
			krok = (TextKaraokeAtomPtr)entry;
			decoder->TextParam->krok_highlight_start_time = krok->highlight_start_time;
			decoder->TextParam->krok_entry_count = krok->entry_count;
			decoder->TextParam->krok_highlight_end_time = calloc(krok->entry_count, sizeof(u32));
			decoder->TextParam->krok_startcharoffset= calloc(krok->entry_count, sizeof(u32));
			decoder->TextParam->krok_endcharoffset = calloc(krok->entry_count, sizeof(u32));
			for(i=0; i<krok->entry_count; i++)
			{
				decoder->TextParam->krok_highlight_end_time[i] =krok->highlight_end_time[i];
				decoder->TextParam->krok_startcharoffset[i] = krok->startcharoffset[i];
				decoder->TextParam->krok_endcharoffset[i] = krok->endcharoffset[i];
			}
		}
		else
		{
			BAILWITHERROR(MP4NotImplementedErr)
		}

		entry->destroy(entry);
	}

	if (is) {
		is->destroy( is );
		is = NULL;
	}

	*param = decoder->TextParam;
#endif

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_GetDsiByIndex(IMAF_DecoderH IMafDecoder,  unsigned int i, char** dsi, unsigned int *dsiSize)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	MP4TrackReader reader;
	MP4Handle specificInfoH;

	err = MP4GetListEntry(decoder->TrackReaderList, i, (char**)&reader); if(err) goto bail;

	err = MP4NewHandle(0, &specificInfoH); if(err) goto bail;
	err = MP4TrackReaderGetCurrentDecoderSpecificInfo(reader, specificInfoH); if(err) goto bail;
	err = MP4GetHandleSize(specificInfoH, dsiSize);
	*dsi = (u8*)malloc(*dsiSize);
	memcpy(*dsi, *specificInfoH, *dsiSize);
	err = MP4DisposeHandle(specificInfoH); if(err) goto bail;
	
bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_GetSongImage(IMAF_DecoderH IMafDecoder, unsigned char **image, unsigned int *imageSize)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;
	u8 *content_type;

	err = MP4GetMovieIndTrack( decoder->moov, 1, &decoder->trak ); if (err) goto bail;
	err = MP4GetTrackMedia( decoder->trak, &decoder->media ); if (err) goto bail;
	err = MP4_GetImageByItemId(decoder->moov, decoder->media, image, imageSize, &content_type, 1); if(err) goto bail;

bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAFDecoder_GetMetadata(IMAF_DecoderH IMafDecoder, unsigned char **Meta, unsigned int *MetaSize)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	err = MP4GetMetaXml(decoder->moov, Meta, MetaSize); if(err) goto bail;
bail:
	TEST_RETURN( err );
	return err;
}

IMAF_DECODER_API IMAF_Err IMAF_Decoder_Destroy(IMAF_DecoderH IMafDecoder)
{
	IMAFDecoder *decoder = (IMAFDecoder*) IMafDecoder;
	IMAF_Err err;

	if(decoder)
	{
		if(decoder->InputMafFilename)
		{
			free(decoder->InputMafFilename);
			decoder->InputMafFilename = NULL;
		}

		if(decoder->TrackReaderList)
		{
			u32 i;
			MP4TrackReader reader;
			MP4Handle sampleH;
			for(i=0; i<decoder->TrackCount - decoder->HasTimedTextTrack; i++)
			{
				err = MP4GetListEntry(decoder->TrackReaderList, i, (char**)&reader); if(err) goto bail;
				err = MP4DisposeTrackReader(reader); if(err) goto bail;
				err = MP4GetListEntry(decoder->TrackSampleList, i, (char**)&sampleH); if(err) goto bail;
				err = MP4DisposeHandle(sampleH); if(err) goto bail;
			}
			err = MP4DeleteLinkedList(decoder->TrackReaderList); if(err) goto bail;
			err = MP4DeleteLinkedList(decoder->TrackSampleList); if(err) goto bail;
			decoder->TrackReaderList = NULL;
			decoder->TrackSampleList = NULL;
		}

		if(decoder->TimedTextSampleH)
			MP4DisposeHandle(decoder->TimedTextSampleH);
		if(decoder->TextH)
			MP4DisposeHandle(decoder->TextH);
		if(decoder->TimedTextTrackReader)
			MP4DisposeTrackReader(decoder->TimedTextTrackReader);
		if(decoder->TextParam)
		{
			InitTextSampleStyleParam(decoder->TextParam);
			free(decoder->TextParam);
		}

		if(decoder->moov)
		{
			err = MP4DisposeMovie( decoder->moov ); if(err) goto bail;
		}
		free(decoder);
	}

bail:
	return err;
}

IMAF_DECODER_API char* IMAF_Decoder_GetLastError(IMAF_Err err)
{
	return MP4GetLastError(err);
}