/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / Authoring Tools sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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 General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */



#include <gpac/intern/m4_author_dev.h>
#include <gpac/intern/avilib.h>
#include <gpac/m4_ogg.h>
#include <gpac/m4_decoder.h>

#include <gpac/m4_mpeg4_nodes.h>

#ifndef M4_READ_ONLY

#define M4TI_DEFAULT_FPS	25.0

static const char *szProg[] = 
{
	"                    ",
	"=                   ",
	"==                  ",
	"===                 ",
	"====                ",
	"=====               ",
	"======              ",
	"=======             ",
	"========            ",
	"=========           ",
	"==========          ",
	"===========         ",
	"============        ",
	"=============       ",
	"==============      ",
	"===============     ",
	"================    ",
	"=================   ",
	"==================  ",
	"=================== ",
	"====================",
};

static u32 prev_pos = 0;
static u32 prev_pc = 0;
void MP4T_OnProgress(void *_title, u32 done, u32 total)
{
	u32 pos = MIN((u32) (20 * ((Float)done)/total), 20);
	char *szT = _title ? (char *)_title : "";
	if (pos>prev_pos) {
		prev_pos = 0;
		prev_pc = 0;
	}
	if (done==total) { 
		u32 len = strlen(szT) + 40;
		while (len) { fprintf(stdout, " "); len--; };
		fprintf(stdout, "\r");
	}
	else {
		u32 pc = (u32) ( 100 * ((Float)done) / total );
		if ((pos!=prev_pos) || (pc!=prev_pc)) {
			prev_pos = pos;
			prev_pc = pc;
			fprintf(stdout, "%s: |%s| (%02d/100)\r", szT, szProg[pos], pc);
			fflush(stdout);
		}
	}
}


M4Err import_message(M4TrackImporter *import, M4Err e, char *format, ...)
{
	va_list args;
	va_start(args, format);
	if (import->import_message) {
		char szMsg[1024];
		vsprintf(szMsg, format, args);
		import->import_message(import, e, szMsg);
	} else {
		vfprintf(stdout,format,args);
		fprintf(stdout, "\n");
		if (e) fprintf(stderr, " Error: %s", M4ErrToString(e));
	}
	va_end(args);
	return e;
}

void import_progress(M4TrackImporter *import, u32 cur_samp, u32 count)
{
	if (import->import_progress) {
		import->import_progress(import, cur_samp, count);
	} else {
		MP4T_OnProgress("Importing", cur_samp, count);
	}
}


static void MP4T_RecomputeBitRate(M4File *file, u32 track)
{
	u32 i, count, timescale, time_wnd, rate;
	Double br;
	ESDescriptor *esd;

	esd = M4_GetStreamDescriptor(file, track, 1);
	if (!esd) return;

	esd->decoderConfig->avgBitrate = 0;
	esd->decoderConfig->maxBitrate = 0;
	rate = time_wnd = 0;

	timescale = M4_GetMediaTimeScale(file, track);
	count = M4_GetSampleCount(file, track);
	for (i=0; i<count; i++) {
		M4Sample *samp = M4_GetSampleInfo(file, track, i+1, NULL, NULL);
		
		if (samp->dataLength>esd->decoderConfig->bufferSizeDB) esd->decoderConfig->bufferSizeDB = samp->dataLength;

		if (esd->decoderConfig->bufferSizeDB < samp->dataLength) esd->decoderConfig->bufferSizeDB = samp->dataLength;
		esd->decoderConfig->avgBitrate += samp->dataLength;
		rate += samp->dataLength;
		if (samp->DTS > time_wnd + timescale) {
			if (rate > esd->decoderConfig->maxBitrate) esd->decoderConfig->maxBitrate = rate;
			time_wnd = samp->DTS;
			rate = 0;
		}

		M4_DeleteSample(&samp);
	}
	
	br = (Double) (s64) M4_GetMediaDuration(file, track);
	br /= timescale;
	esd->decoderConfig->avgBitrate = (u32) (esd->decoderConfig->avgBitrate / br);
	/*move to bps*/
	esd->decoderConfig->avgBitrate *= 8;
	esd->decoderConfig->maxBitrate *= 8;

	M4_ChangeStreamDescriptor(file, track, 1, esd);
	OD_DeleteDescriptor((Descriptor **)&esd);
}

static void get_video_timing(Double fps, u32 *timescale, u32 *dts_inc)
{
	u32 fps_1000 = (u32) (fps*1000);
	/*handle all drop-frame formats*/
	if (fps_1000==29970) {
		*timescale = 30000;
		*dts_inc = 1001;
	}
	else if (fps_1000==23976) {
		*timescale = 24000;
		*dts_inc = 1001;
	}
	else if (fps_1000==59940) {
		*timescale = 60000;
		*dts_inc = 1001;
	} else {
		*timescale = fps_1000;
		*dts_inc = 1000;
	}
}


void MP4T_GetImageSize(BitStream *bs, u8 OTI, u32 *width, u32 *height)
{
	u64 pos;
	pos = BS_GetPosition(bs);
	BS_Seek(bs, 0);
	
	*width = *height = 0;
	switch (OTI) {
	case 0x6C:
		/*check for SOI marker and JFIF marker*/
		if (BS_ReadU8(bs) != 0xFF) goto exit;
		if (BS_ReadU8(bs) != 0xD8) goto exit;
		if (BS_ReadU8(bs) != 0xFF) goto exit;
		BS_ReadU8(bs);
		/*get frame header FFC0*/
		while (BS_Available(bs)) {
			u32 type, w, h;
			if (BS_ReadU8(bs) != 0xFF) continue;
			type = BS_ReadU8(bs);
			switch (type) {
			case 0xC0:
			case 0xC1:
			case 0xC2:
				BS_SkipBytes(bs, 3);
				h = BS_ReadInt(bs, 16);
				w = BS_ReadInt(bs, 16);
				if ((w > *width) || (h > *height)) {
					*width = w;
					*height = h;
				}
				break;
			case 0xD0:
			case 0xD1:
			case 0xD2:
			case 0xD3:
			case 0xD4:
			case 0xD5:
			case 0xD6:
			case 0xD7:
				break;
			}
		}
		break;
	case 0x6D:
		/*check for PNG sig*/
		if ( (BS_ReadU8(bs) != 0x89) || (BS_ReadU8(bs) != 0x50) || (BS_ReadU8(bs) != 0x4E) 
			|| (BS_ReadU8(bs) != 0x47) || (BS_ReadU8(bs) != 0x0D) || (BS_ReadU8(bs) != 0x0A) 
			|| (BS_ReadU8(bs) != 0x1A) || (BS_ReadU8(bs) != 0x0A) ) goto exit;
		BS_ReadU32(bs);
		/*check for PNG IHDR*/
		if ( (BS_ReadU8(bs) != 'I') || (BS_ReadU8(bs) != 'H') 
			|| (BS_ReadU8(bs) != 'D') || (BS_ReadU8(bs) != 'R')) goto exit;

		*width = BS_ReadU32(bs);
		*height = BS_ReadU32(bs);
		break;
	default:
		break;
	}

exit:
	BS_Seek(bs, pos);
}

static M4Err MP4T_ImportStillImage(M4TrackImporter *import, u8 OTI)
{
	BitStream *bs;
	M4Err e;
	Bool destroy_esd;
	u32 size, track, di, w, h;
	M4Sample *samp;
	ESDescriptor *esd;
	unsigned char *data;
	FILE *src;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_VisualMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_NO_DURATION;
		import->nb_tracks = 1;
		return M4OK;
	}

	src = fopen(import->in_name, "rb");
	if (!src) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	fseek(src, 0, SEEK_END);
	size = ftell(src);
	fseek(src, 0, SEEK_SET);
	data = malloc(sizeof(unsigned char)*size);
	fread(data, sizeof(unsigned char)*size, 1, src);
	fclose(src);

	/*get image size*/
	bs = NewBitStream(data, size, BS_READ);
	MP4T_GetImageSize(bs, OTI, &w, &h);
	DeleteBitStream(bs);

	if (!w || !h) {
		free(data);
		return import_message(import, M4NonCompliantBitStream, "Invalid %s file", (OTI==0x6C) ? "JPEG" : "PNG");
	}

	e = M4OK;
	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		esd = OD_NewESDescriptor(2);
		destroy_esd = 1;
	}
	/*update stream type/oti*/
	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	esd->decoderConfig->streamType = M4ST_VISUAL;
	esd->decoderConfig->objectTypeIndication = OTI;
	esd->decoderConfig->bufferSizeDB = size;
	esd->decoderConfig->avgBitrate = 8*size;
	esd->decoderConfig->maxBitrate = 8*size;
	esd->slConfig->timestampResolution = 1000;
	
	track = M4_NewTrack(import->dest, esd->ESID, M4_VisualMediaType, 1000);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;

	e = M4_NewStreamDescription(import->dest, track, esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
	if (e) goto exit;
	M4_SetVisualEntrySize(import->dest, track, di, w, h);
	samp = M4_NewSample();
	samp->IsRAP = 1;
	samp->dataLength = size;

	import_message(import, M4OK, "%s import - size %d x %d", (OTI==0x6C) ? "JPEG" : "PNG", w, h);

	import_progress(import, 0, 1);
	if (import->flags & M4TI_USE_DATAREF) {
		e = M4_AddSampleReference(import->dest, track, di, samp, (u64) 0);
	} else {
		samp->data = data;
		e = M4_AddSample(import->dest, track, di, samp);
		samp->data = NULL;
	}
	import_progress(import, 1, 1);

	M4_DeleteSample(&samp);

exit:
	free(data);
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	return e;
}


M4Err MP4T_ImportMP3(M4TrackImporter *import)
{
	u8 oti;
	Bool destroy_esd;
	M4Err e;
	ESDescriptor *esd;
	u16 sr;
	u32 nb_chan;
	FILE *in;
	u32 hdr, size, max_size, track, di, tot_size, done, offset, duration;
	M4Sample *samp;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_AudioMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF;
		import->nb_tracks = 1;
		return M4OK;
	}

	in = fopen(import->in_name, "rb");
	if (!in) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	hdr = MP3_GetNextHeader(in);
	if (!hdr) {
		fclose(in);
		return import_message(import, M4NonCompliantBitStream, "Audio isn't MPEG-1/2 audio");
	}
	sr = MP3_GetSamplingRate(hdr);
	oti = MP3_GetObjectTypeIndication(hdr);
	if (!oti) {
		fclose(in);
		return import_message(import, M4NonCompliantBitStream, "Audio isn't MPEG-1/2 audio");
	}

	e = M4OK;
	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		esd = OD_NewESDescriptor(2);
		destroy_esd = 1;
	}
	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	/*update stream type/oti*/
	esd->decoderConfig->streamType = M4ST_AUDIO;
	esd->decoderConfig->objectTypeIndication = oti;
	esd->decoderConfig->bufferSizeDB = 20;
	esd->slConfig->timestampResolution = sr;

	samp = NULL;
	nb_chan = MP3_GetNumChannels(hdr);
	import_message(import, M4OK, "MP3 import - sample rate %d - %s audio - %d channel%s", sr, (oti==0x6B) ? "MPEG-1" : "MPEG-2", nb_chan, (nb_chan>1) ? "s" : "");

	track = M4_NewTrack(import->dest, esd->ESID, M4_AudioMediaType, sr);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;
	if (esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &esd->decoderConfig->decoderSpecificInfo);
	M4_NewStreamDescription(import->dest, track, esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
	M4_SetAudioDescriptionInfo(import->dest, track, di, sr, nb_chan, 16);

	fseek(in, 0, SEEK_END);
	tot_size = ftell(in);
	fseek(in, 0, SEEK_SET);

	e = M4OK;
	samp = M4_NewSample();
	samp->IsRAP = 1;

	duration = import->duration*sr;
	duration /= 1000;

	max_size = 0;
	done = 0;
	while (tot_size > done) {
		/* get the next MP3 frame header */
		hdr = MP3_GetNextHeader(in);
		/*MP3 stream truncated*/
		if (!hdr) break;

		offset = ftell(in) - 4;
		size = MP3_GetFrameSize(hdr);
		if (size>max_size) {
			samp->data = realloc(samp->data, sizeof(char) * size);
			max_size = size;
		}
		samp->data[0] = (hdr >> 24) & 0xFF;
		samp->data[1] = (hdr >> 16) & 0xFF;
		samp->data[2] = (hdr >> 8) & 0xFF;
		samp->data[3] = hdr & 0xFF;
		samp->dataLength = size;

		/* read the frame data into the buffer */
		if (fread(&samp->data[4], 1, size - 4, in) != size - 4) break;

		if (import->flags & M4TI_USE_DATAREF) {
			M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			M4_AddSample(import->dest, track, di, samp);
		}

		import_progress(import, done, tot_size);

		samp->DTS += MP3_GetSamplesPerFrame(hdr);
		done += samp->dataLength;
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	MP4T_RecomputeBitRate(import->dest, track);
	import_progress(import, tot_size, tot_size);

exit:
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	if (samp) M4_DeleteSample(&samp);
	fclose(in);
	return e;
}

typedef struct
{
	Bool is_mp2, no_crc;
	u32 profile, sr_idx, nb_ch, frame_size;
} ADTSHeader;

static Bool ADTS_SyncFrame(BitStream *bs, ADTSHeader *hdr)
{
	u32 val, hdr_size, pos;
	while (BS_Available(bs)) {
		val = BS_ReadU8(bs);
		if (val!=0xFF) continue;
		val = BS_ReadInt(bs, 4);
		if (val != 0x0F) {
			BS_ReadInt(bs, 4);
			continue;
		}
		hdr->is_mp2 = BS_ReadInt(bs, 1);
		BS_ReadInt(bs, 2);
		hdr->no_crc = BS_ReadInt(bs, 1);
		pos = (u32) BS_GetPosition(bs) - 2;

		hdr->profile = 1 + BS_ReadInt(bs, 2);
		hdr->sr_idx = BS_ReadInt(bs, 4);
		BS_ReadInt(bs, 1);
		hdr->nb_ch = BS_ReadInt(bs, 3);
		BS_ReadInt(bs, 4);
		hdr->frame_size = BS_ReadInt(bs, 13);
		BS_ReadInt(bs, 11);
		BS_ReadInt(bs, 2);
		hdr_size = hdr->no_crc ? 7 : 9;
		if (!hdr->no_crc) BS_ReadInt(bs, 16);
		if (hdr->frame_size < hdr_size) {
			BS_Seek(bs, pos+1);
			continue;
		}
		hdr->frame_size -= hdr_size;
		if (BS_Available(bs) == hdr->frame_size) return 1;

		BS_SkipBytes(bs, hdr->frame_size);
		val = BS_ReadU8(bs);
		if (val!=0xFF) {
			BS_Seek(bs, pos+1);
			continue;
		}
		val = BS_ReadInt(bs, 4);
		if (val!=0x0F) {
			BS_ReadInt(bs, 4);
			BS_Seek(bs, pos+2);
			continue;
		}
		BS_Seek(bs, pos+hdr_size);
		return 1;
	}
	return 0;
}

M4Err MP4T_ImportAAC_ADTS(M4TrackImporter *import)
{
	u8 oti;
	Bool destroy_esd;
	M4Err e;
	ESDescriptor *esd;
	Bool sync_frame;
	u16 sr, sbr_sr, sbr_sr_idx;
	BitStream *bs, *dsi;
	ADTSHeader hdr;
	M4ADecoderSpecificInfo acfg;
	FILE *in;
	u64 offset;
	u32 max_size, track, di, tot_size, done, duration, prof, i;
	M4Sample *samp;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_AudioMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_SBR_IMPLICIT | M4TI_SBR_EXPLICIT;
		import->nb_tracks = 1;
		return M4OK;
	}

	in = fopen(import->in_name, "rb");
	if (!in) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	bs = NewBitStreamFromFile(in, BS_FILE_READ);

	sync_frame = ADTS_SyncFrame(bs, &hdr);
	if (!sync_frame) {
		DeleteBitStream(bs);
		fclose(in);
		return import_message(import, M4NonCompliantBitStream, "Audio isn't MPEG-2/4 AAC with ADTS");
	}
	/*keep MPEG-2 AAC OTI even for HE-SBR (that's correct according to latest MPEG-4 audio spec)*/
	oti = hdr.is_mp2 ? hdr.profile+0x66-1 : 0x40;
	sr = m4a_sample_rates[hdr.sr_idx];


	dsi = NewBitStream(NULL, 0, BS_WRITE);

	sbr_sr = sr;
	sbr_sr_idx = hdr.sr_idx;
	for (i=0; i<16; i++) {
		if (m4a_sample_rates[i] == (u32) 2*sr) {
			sbr_sr_idx = i;
			sbr_sr = 2*sr;
			break;
		}
	}
	/*no provision for explicit indication of MPEG-2 AAC through MPEG-4 PLs, so force implicit*/
	if (hdr.is_mp2) {
		if (import->flags & M4TI_SBR_EXPLICIT) {
			import->flags &= ~M4TI_SBR_EXPLICIT;
			import->flags |= M4TI_SBR_IMPLICIT;
		}
	}

	memset(&acfg, 0, sizeof(M4ADecoderSpecificInfo));
	acfg.base_object_type = hdr.profile;
	acfg.base_sr = sr;
	acfg.nb_chan = hdr.nb_ch;
	acfg.sbr_object_type = 0;
	if (import->flags & M4TI_SBR_EXPLICIT) {
		acfg.has_sbr = 1;
		acfg.base_object_type = 5;
		acfg.sbr_object_type = hdr.profile;
	} else if (import->flags & M4TI_SBR_IMPLICIT) {
		acfg.has_sbr = 1;
	}
	acfg.audioPL = M4A_GetProfile(&acfg);
	/*explicit SBR signal (non backward-compatible)*/
	if (import->flags & M4TI_SBR_EXPLICIT) {
		BS_WriteInt(dsi, 5, 5);
		BS_WriteInt(dsi, hdr.sr_idx, 4);
		BS_WriteInt(dsi, hdr.nb_ch, 4);
		BS_WriteInt(dsi, sbr_sr ? sbr_sr_idx : hdr.sr_idx, 4);
		BS_WriteInt(dsi, hdr.profile, 5);
	} else {
		/*regular AAC*/
		BS_WriteInt(dsi, hdr.profile, 5);
		BS_WriteInt(dsi, hdr.sr_idx, 4);
		BS_WriteInt(dsi, hdr.nb_ch, 4);
		BS_Align(dsi);
		/*implicit AAC SBR signal*/
		if (import->flags & M4TI_SBR_IMPLICIT) {
			BS_WriteInt(dsi, 0x2b7, 11); /*sync extension type*/
			BS_WriteInt(dsi, 5, 5);	/*SBR objectType*/
			BS_WriteInt(dsi, 1, 1);	/*SBR present flag*/
			BS_WriteInt(dsi, sbr_sr_idx, 4);
		}
	}
	/*not MPEG4 tool*/
	if (0 && hdr.is_mp2) acfg.audioPL = 0xFE;

	BS_Align(dsi);
	prof = hdr.profile;

	e = M4OK;
	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		esd = OD_NewESDescriptor(2);
		destroy_esd = 1;
	}
	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	esd->decoderConfig->streamType = M4ST_AUDIO;
	esd->decoderConfig->objectTypeIndication = oti;
	esd->decoderConfig->bufferSizeDB = 20;
	esd->slConfig->timestampResolution = sr;
	if (!esd->decoderConfig->decoderSpecificInfo) esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
	if (esd->decoderConfig->decoderSpecificInfo->data) free(esd->decoderConfig->decoderSpecificInfo->data);
	BS_GetContent(dsi, (unsigned char **) &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
	DeleteBitStream(dsi);

	samp = NULL;
	import_message(import, M4OK, "AAC import %s- sample rate %d - %s audio - %d channel%s", (import->flags & M4TI_SBR_IMPLICIT) ? "SBR (implicit) " : ((import->flags & M4TI_SBR_EXPLICIT) ? "SBR (explicit) " : ""), sr, (oti==0x40) ? "MPEG-4" : "MPEG-2", hdr.nb_ch, (hdr.nb_ch>1) ? "s" : "");

	track = M4_NewTrack(import->dest, esd->ESID, M4_AudioMediaType, sr);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;
	M4_NewStreamDescription(import->dest, track, esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
	M4_SetAudioDescriptionInfo(import->dest, track, di, sr, (hdr.nb_ch>1) ? 2 : 1, 16);

	e = M4OK;
	/*add first sample*/
	samp = M4_NewSample();
	samp->IsRAP = 1;
	max_size = samp->dataLength = hdr.frame_size;
	samp->data = malloc(sizeof(char)*hdr.frame_size);
	offset = BS_GetPosition(bs);
	BS_ReadData(bs, samp->data, hdr.frame_size);

	if (import->flags & M4TI_USE_DATAREF) {
		M4_AddSampleReference(import->dest, track, di, samp, offset);
	} else {
		M4_AddSample(import->dest, track, di, samp);
	}
	samp->DTS+=1024;

	duration = import->duration*sr;
	duration /= 1000;

	tot_size = (u32) BS_GetSize(bs);
	done = 0;
	while (BS_Available(bs) ) {
		sync_frame = ADTS_SyncFrame(bs, &hdr);
		if (!sync_frame) break;
		if (hdr.frame_size>max_size) {
			samp->data = realloc(samp->data, sizeof(char) * hdr.frame_size);
			max_size = hdr.frame_size;
		}
		samp->dataLength = hdr.frame_size;

		offset = BS_GetPosition(bs);
		BS_ReadData(bs, samp->data, hdr.frame_size);
		if (import->flags & M4TI_USE_DATAREF) {
			M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			M4_AddSample(import->dest, track, di, samp);
		}

		import_progress(import, done, tot_size);
		samp->DTS += 1024;
		done += samp->dataLength;
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	MP4T_RecomputeBitRate(import->dest, track);
	M4_SetMoviePLIndication(import->dest, M4_PL_AUDIO, acfg.audioPL);
	import_progress(import, tot_size, tot_size);

exit:
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	if (samp) M4_DeleteSample(&samp);
	DeleteBitStream(bs);
	fclose(in);
	return e;
}

LPM4VPARSER M4V_NewParserBitStream(BitStream *bs);

M4Err MP4T_ImportCMP(M4TrackImporter *import)
{
	M4Err e;
	Double FPS;
	FILE *mdia;
	M4Sample *samp;
	u32 nb_samp, i, timescale, max_size, samp_offset, track, di, PL, max_b, nbI, nbP, nbB, nbNotCoded, tot_size, done_size, dts_inc, ref_frame, b_frames;
	Bool is_vfr, enable_vfr, erase_pl, has_cts_offset, is_packed, destroy_esd, do_vfr, forced_packed;
	M4VDecoderSpecificInfo dsi;
	LPM4VPARSER vparse;
	BitStream *bs;
	u64 pos;
	u32 duration;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_VisualMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_NO_FRAME_DROP | M4TI_OVERRIDE_FPS | M4TI_FORCE_PACKED;
		import->nb_tracks = 1;
		return M4OK;
	}

	mdia = fopen(import->in_name, "rb");
	if (!mdia) return import_message(import, M4URLNotFound, "Opening %s failed", import->in_name);
	bs = NewBitStreamFromFile(mdia, BS_FILE_READ);

	tot_size = (u32) BS_GetSize(bs);
	done_size = 0;
	destroy_esd = 0;
	FPS = M4TI_DEFAULT_FPS;
	if (import->video_fps) FPS = (Double) import->video_fps;
	get_video_timing(FPS, &timescale, &dts_inc);

	duration = (u32) (import->duration*FPS);

	is_packed = 0;
	nbNotCoded = nbI = nbP = nbB = max_b = 0;
	enable_vfr = is_vfr = erase_pl = 0;
	samp = M4_NewSample();
	max_size = 4096;
	samp->data = malloc(sizeof(char)*max_size);

	vparse = M4V_NewParserBitStream(bs);
	e = M4V_ParseConfig(vparse, &dsi);
	if (e) {
		import_message(import, e, "Cannot load MPEG-4 decoder config");
		goto exit;
	}
	PL = dsi.VideoPL;
	if (!PL) {
		PL = 0x01;
		erase_pl = 1;
	}
	samp_offset = M4V_GetObjectStartPos(vparse);
	if (!import->esd) {
		import->esd = OD_NewESDescriptor(0);
		destroy_esd = 1;
	}
	track = M4_NewTrack(import->dest, import->esd->ESID, M4_VisualMediaType, timescale);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	if (!import->esd->slConfig) import->esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	import->esd->slConfig->timestampResolution = timescale;
	if (!import->esd->decoderConfig) import->esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (import->esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &import->esd->decoderConfig->decoderSpecificInfo);
	import->esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
	import->esd->decoderConfig->streamType = M4ST_VISUAL;
	import->esd->decoderConfig->objectTypeIndication = 0x20;
	import->esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char) * samp_offset);
	import->esd->decoderConfig->decoderSpecificInfo->dataLength = samp_offset;
	pos = BS_GetPosition(bs);
	BS_Seek(bs, 0);
	BS_ReadData(bs, import->esd->decoderConfig->decoderSpecificInfo->data, samp_offset);
	BS_Seek(bs, pos);

	/*remove packed flag if any (VOSH user data)*/
	forced_packed = 0;
	i=0;
	while (1) {
		char *frame = import->esd->decoderConfig->decoderSpecificInfo->data;
		while ((i+3<samp_offset)  && ((frame[i]!=0) || (frame[i+1]!=0) || (frame[i+2]!=1))) i++;
		if (i+4>=samp_offset) break;
		if (strncmp(frame+i+4, "DivX", 4)) {
			i += 4;
			continue;
		}
		frame = import->esd->decoderConfig->decoderSpecificInfo->data + i + 4;
		frame = strchr(frame, 'p');
		if (frame) {
			forced_packed = 1;
			frame[0] = 'n';
		}
		break;
	}

	if (import->flags & M4TI_FORCE_PACKED) forced_packed = 1;

	M4_SetCTSPackMode(import->dest, track, 1);

	e = M4_NewStreamDescription(import->dest, track, import->esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name: NULL, NULL, &di);
	if (e) goto exit;
	M4_SetVisualEntrySize(import->dest, track, di, dsi.width, dsi.height);
	import_message(import, M4OK, "MPEG-4 Video import - %d x %d @ %02.4f FPS\nIndicated Profile: %s", dsi.width, dsi.height, FPS, M4V_VideoProfileName((u8) PL));
	has_cts_offset = 0;
	nb_samp = b_frames = ref_frame = 0;
	do_vfr = !(import->flags & M4TI_NO_FRAME_DROP);

	while (BS_Available(bs)) {
		u8 ftype;
		u32 tinc, frame_start;
		Bool is_coded;
		pos = M4V_GetObjectStartPos(vparse);
		e = M4V_GetFrame(vparse, dsi, &ftype, &tinc, &samp->dataLength, &frame_start, &is_coded);
		if (e==M4EOF) e = M4OK;
		if (e) goto exit;

		if (!is_coded) {
			nbNotCoded ++;
			/*if prev is B and we're parsing a packed bitstream discard n-vop*/
			if (forced_packed && b_frames) {
				is_packed = 1;
				continue;
			}
			/*policy is to import at variable frame rate, skip*/
			if (do_vfr) {
				is_vfr = 1;
				samp->DTS += dts_inc;
				continue;
			}
			/*policy is to keep non coded frame (constant frame rate), add*/
		} 
		samp->IsRAP = 0;

		if (ftype==2) {
			b_frames++;
			nbB++;
			/*adjust CTS*/
			if (!has_cts_offset) {
				u32 i;
				for (i=0; i<M4_GetSampleCount(import->dest, track); i++) {
					M4_SetSampleCTSOffset(import->dest, track, i+1, dts_inc);
				}
				has_cts_offset = 1;
			}
		} else {
			if (ftype==0) {
				samp->IsRAP = 1;
				nbI++;
			} else {
				nbP++;
			}
			/*even if no out-of-order frames we MUST adjust CTS if cts_offset is present is */
			if (ref_frame && has_cts_offset) 
				M4_SetSampleCTSOffset(import->dest, track, ref_frame, (1+b_frames)*dts_inc);

			ref_frame = nb_samp+1;
			if (max_b<b_frames) max_b = b_frames;
			b_frames = 0;
		}
		if (import->flags & M4TI_USE_DATAREF) {
			samp->data = NULL;
			e = M4_AddSampleReference(import->dest, track, di, samp, frame_start);
		} else {
			if (samp->dataLength>max_size) {
				max_size = samp->dataLength;
				samp->data = realloc(samp->data, sizeof(char)*max_size);
			}
			BS_Seek(bs, frame_start);
			BS_ReadData(bs, samp->data, samp->dataLength);
			e = M4_AddSample(import->dest, track, di, samp);
		}
		samp->DTS += dts_inc;
		nb_samp++;
		import_progress(import, done_size, tot_size);
		done_size += samp->dataLength;
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	/*final flush*/
	if (ref_frame && has_cts_offset) 
		M4_SetSampleCTSOffset(import->dest, track, ref_frame, (1+b_frames)*dts_inc);

	import_progress(import, nb_samp, nb_samp);
	if (has_cts_offset) {
		import_message(import, M4OK, "Has B-Frames (%d max consecutive B-VOPs)", max_b);
		M4_SetCTSPackMode(import->dest, track, 0);
		/*this is plain ugly but since some encoders (divx) don't use the video PL correctly 
		we force the system video_pl to ASP@L5 since we have I, P, B in base layer*/
		if (PL<=3) {
			PL = 0xF5;
			erase_pl = 1;
			import_message(import, M4OK, "WARNING: indicated profile doesn't include B-VOPs - forcing %s", M4V_VideoProfileName((u8) PL));
		}
		import_message(import, M4OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", nb_samp, nbI, nbP, nbB);
	} else {
		/*no B-frames, remove CTS offsets*/
		M4_DeleteCTSTable(import->dest, track);
		import_message(import, M4OK, "Import results: %d VOPs (%d Is - %d Ps)", nb_samp, nbI, nbP);
	}
	if (erase_pl) {
		M4V_RewritePL((unsigned char **) &import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength, (u8) PL);
		M4_ChangeStreamDescriptor(import->dest, track, 1, import->esd);
	}
	MP4T_RecomputeBitRate(import->dest, track);

	if (is_vfr) {
		if (!nbB) {
			if (do_vfr) {
				import_message(import, M4OK, "import using Variable Frame Rate - Removed %d N-VOPs", nbNotCoded);
			} else {
				if (nbNotCoded) import_message(import, M4OK, "Stream has %d N-VOPs", nbNotCoded);
			}
			nbNotCoded = 0;
		}
	}

	if (nbNotCoded) import_message(import, M4OK, "Removed %d N-VOPs%s", nbNotCoded,is_packed ? " (Packed Bitstream)" : "");
	M4_SetMoviePLIndication(import->dest, M4_PL_VISUAL, (u8) PL);

exit:
	M4_DeleteSample(&samp);
	if (destroy_esd) OD_DeleteDescriptor((Descriptor **) &import->esd);
	M4V_DeleteParser(vparse);
	fclose(mdia);
	return e;
}

M4Err MP4T_ImportAVIVideo(M4TrackImporter *import)
{
	M4Err e;
	Double FPS;
	FILE *test;
	M4Sample *samp;
	u32 i, num_samples, timescale, size, max_size, samp_offset, track, di, PL, max_b, nb_f, ref_frame, b_frames;
	u32 nbI, nbP, nbB, nbDummy, nbNotCoded, dts_inc, cur_samp;
	Bool is_vfr, enable_vfr, erase_pl;
	M4VDecoderSpecificInfo dsi;
	LPM4VPARSER vparse;
	s32 key;
	u32 duration;
	Bool destroy_esd, is_packed, is_init, has_cts_offset;
	char *comp, *frame;
	avi_t *in;

	if (import->trackID>1) return M4OK;

	test = fopen(import->in_name, "rb");
	if (!test) return import_message(import, M4URLNotFound, "Opening %s failed", import->in_name);
	fclose(test);
	in = AVI_open_input_file(import->in_name, 1);
	if (!in) return import_message(import, M4NonCompliantBitStream, "Unsupported avi file");

	if (import->flags & M4TI_PROBE_ONLY) {
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_VisualMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_NO_FRAME_DROP | M4TI_OVERRIDE_FPS;
		import->nb_tracks = 1;
		for (i=0; i<(u32) AVI_audio_tracks(in); i++) {
			import->tk_info[i+1].track_num = i+2;
			import->tk_info[i+1].type = M4_AudioMediaType;
			import->tk_info[i+1].flags = M4TI_USE_DATAREF;
			import->nb_tracks ++;
		}
		AVI_close(in);
		return M4OK;
	}

	destroy_esd = 0;
	frame = NULL;
	AVI_seek_start(in);

	erase_pl = 0;
	comp = AVI_video_compressor(in);
	if (!comp) {
		e = M4NotSupported;
		goto exit;
	}

	/*these are/should be OK*/
	if (!stricmp(comp, "DIVX") || !stricmp(comp, "DX50")	/*DivX*/
		|| !stricmp(comp, "XVID") /*XviD*/
		|| !stricmp(comp, "3iv2") /*3ivX*/
		|| !stricmp(comp, "fvfw") /*ffmpeg*/
		|| !stricmp(comp, "NDIG") /*nero*/
		|| !stricmp(comp, "MP4V") /*!! not tested*/
		|| !stricmp(comp, "M4CC") /*Divio - not tested*/
		|| !stricmp(comp, "PVMM") /*PacketVideo - not tested*/
		|| !stricmp(comp, "SEDG") /*Samsung - not tested*/
		|| !stricmp(comp, "RMP4") /*Sigma - not tested*/
		) {
		e = M4OK;
	}
	else if (!stricmp(comp, "DIV3") || !stricmp(comp, "DIV4")) {
		import_message(import, M4NotSupported, "Video format %s not compliant with MPEG-4 Visual - please recompress the file first", comp);
		e = M4NotSupported;
		goto exit;
	} else {
		import_message(import, M4NotSupported, "Video format %s not supported - recompress the file first", comp);
		e = M4NotSupported;
		goto exit;
	}

	FPS = AVI_frame_rate(in);
	if (import->video_fps) FPS = (Double) import->video_fps;
	get_video_timing(FPS, &timescale, &dts_inc);
	duration = (u32) (import->duration*FPS);

	e = M4OK;
	max_size = 0;
	samp_offset = 0;
	frame = NULL;
	num_samples = AVI_video_frames(in);
	samp = M4_NewSample();
	PL = 0;
	track = 0;
	is_vfr = 0;

	is_packed = 0;
	nbDummy = nbNotCoded = nbI = nbP = nbB = max_b = 0;
	enable_vfr = 0;
	has_cts_offset = 0;
	cur_samp = b_frames = ref_frame = 0;

	is_init = 0;

	for (i=0; i<num_samples; i++) {
		size = AVI_frame_size(in, i);
		if (!size) {
			AVI_read_frame(in, NULL, &key);
			continue;
		}

		if (size > max_size) {
			frame = realloc(frame, sizeof(char) * size);
			max_size = size;
		}
		AVI_read_frame(in, frame, &key);

		/*get DSI*/
		if (!is_init) {
			is_init = 1;
			vparse = M4V_NewParser(frame, size);
			e = M4V_ParseConfig(vparse, &dsi);
			PL = dsi.VideoPL;
			if (!PL) {
				PL = 0x01;
				erase_pl = 1;
			}
			samp_offset = M4V_GetObjectStartPos(vparse);
			M4V_DeleteParser(vparse);
			if (e) {
				import_message(import, e, "Cannot import decoder config in first frame");
				goto exit;
			}

			if (!import->esd) {
				import->esd = OD_NewESDescriptor(0);
				destroy_esd = 1;
			}
			track = M4_NewTrack(import->dest, import->esd->ESID, M4_VisualMediaType, timescale);
			if (!track) {
				e = M4_GetLastError(import->dest);
				goto exit;
			}
			M4_SetTrackEnabled(import->dest, track, 1);
			if (!import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
			import->final_trackID = M4_GetTrackID(import->dest, track);
			
			if (!import->esd->slConfig) import->esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
			import->esd->slConfig->timestampResolution = timescale;

			if (!import->esd->decoderConfig) import->esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
			if (import->esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &import->esd->decoderConfig->decoderSpecificInfo);
			import->esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
			import->esd->decoderConfig->streamType = M4ST_VISUAL;
			import->esd->decoderConfig->objectTypeIndication = 0x20;
			import->esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char) * samp_offset);
			memcpy(import->esd->decoderConfig->decoderSpecificInfo->data, frame, sizeof(char) * samp_offset);
			import->esd->decoderConfig->decoderSpecificInfo->dataLength = samp_offset;

			M4_SetCTSPackMode(import->dest, track, 1);

			/*remove packed flag if any (VOSH user data)*/
			while (1) {
				char *divx_mark;
				while ((i+3<samp_offset)  && ((frame[i]!=0) || (frame[i+1]!=0) || (frame[i+2]!=1))) i++;
				if (i+4>=samp_offset) break;

				if (strncmp(frame+i+4, "DivX", 4)) {
					i += 4;
					continue;
				}
				divx_mark = import->esd->decoderConfig->decoderSpecificInfo->data + i + 4;
				divx_mark = strchr(divx_mark, 'p');
				if (divx_mark) divx_mark[0] = 'n';
				break;
			}
			i = 0;
			
			e = M4_NewStreamDescription(import->dest, track, import->esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name: NULL, NULL, &di);
			if (e) goto exit;
			M4_SetVisualEntrySize(import->dest, track, di, dsi.width, dsi.height);
			import_message(import, M4OK, "AVI %s video import - %d x %d @ %.2f FPS - %d Frames\nIndicated Profile: %s", comp, dsi.width, dsi.height, FPS, num_samples, M4V_VideoProfileName((u8) PL));
		}


		if (size > samp_offset) {
			u8 ftype;
			u32 tinc, framesize, frame_start;
			u64 file_offset;
			Bool is_coded;

			size -= samp_offset;
			file_offset = (u64) AVI_get_video_position(in, i);

			vparse = M4V_NewParser(frame + samp_offset, size);

			samp->dataLength = 0;
			/*removing padding frames*/
			if (size<4) {
				nbDummy ++;
				size = 0;
			}

			nb_f=0;
			while (size) {
				M4Err e = M4V_GetFrame(vparse, dsi, &ftype, &tinc, &framesize, &frame_start, &is_coded);
				if (e<0) goto exit;

				if (!is_coded) {
					if (!M4V_IsValidObjectType(vparse)) import_message(import, M4OK, "WARNING: AVI frame %d doesn't look like MPEG-4 Visual", i+1);
					nbNotCoded ++;
					if (!is_packed) {
						is_vfr = 1;
						/*policy is to import at constant frame rate from AVI*/
						if (import->flags & M4TI_NO_FRAME_DROP) goto proceed;
						/*policy is to import at variable frame rate from AVI*/
						samp->DTS += dts_inc;
					}
					/*assume this is packed bitstream n-vop and discard it.*/
				} else {
proceed:
					if (e==M4EOF) size = 0;
					else is_packed = 1;
					nb_f++;

					samp->IsRAP = 0;

					if (ftype==2) {
						b_frames ++;
						nbB++;
						/*adjust CTS*/
						if (!has_cts_offset) {
							u32 i;
							for (i=0; i<M4_GetSampleCount(import->dest, track); i++) {
								M4_SetSampleCTSOffset(import->dest, track, i+1, dts_inc);
							}
							has_cts_offset = 1;
						}
					} else {
						if (!ftype) {
							samp->IsRAP = 1;
							nbI++;
						} else {
							nbP++;
						}
						/*even if no out-of-order frames we MUST adjust CTS if cts_offset is present is */
						if (ref_frame && has_cts_offset) 
							M4_SetSampleCTSOffset(import->dest, track, ref_frame, (1+b_frames)*dts_inc);

						ref_frame = cur_samp+1;
						if (max_b<b_frames) max_b = b_frames;
						b_frames = 0;
					}
					/*frame_start indicates start of VOP (eg we always remove VOL from each I)*/
					samp->data = frame + samp_offset + frame_start;
					samp->dataLength = framesize;

					if (import->flags & M4TI_USE_DATAREF) {
						samp->data = NULL;
						e = M4_AddSampleReference(import->dest, track, di, samp, file_offset + samp_offset + frame_start);
					} else {
						e = M4_AddSample(import->dest, track, di, samp);
					}
					cur_samp++;
					samp->DTS += dts_inc;
					if (e) import_message(import, M4OK, "Error importing AVI frame %d", i+1);
				}
				if (!size || (size == framesize + frame_start)) break;
			}
			M4V_DeleteParser(vparse);
			if (nb_f>2) import_message(import, M4OK, "Warning: more than 2 frames packed together");
		}
		samp_offset = 0;
		import_progress(import, i, num_samples);
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) 
			break;
	}

	/*final flush*/
	if (ref_frame && has_cts_offset) 
		M4_SetSampleCTSOffset(import->dest, track, ref_frame, (1+b_frames)*dts_inc);

	import_progress(import, num_samples, num_samples);


	num_samples = M4_GetSampleCount(import->dest, track);
	if (has_cts_offset) {
		import_message(import, M4OK, "Has B-Frames (%d max consecutive B-VOPs%s)", max_b, is_packed ? " - packed bitstream" : "");
		/*repack CTS tables and adjust offsets for B-frames*/
		M4_SetCTSPackMode(import->dest, track, 0);
		/*this is plain ugly but since some encoders (divx) don't use the video PL correctly 
		we force the system video_pl to ASP@L5 since we have I, P, B in base layer*/
		if (PL<=3) {
			PL = 0xF5;
			erase_pl = 1;
			import_message(import, M4OK, "WARNING: indicated profile doesn't include B-VOPs - forcing %s", M4V_VideoProfileName((u8) PL));
		}
		import_message(import, M4OK, "Import results: %d VOPs (%d Is - %d Ps - %d Bs)", num_samples, nbI, nbP, nbB);
	} else {
		/*no B-frames, remove CTS offsets*/
		M4_DeleteCTSTable(import->dest, track);
		import_message(import, M4OK, "Import results: %d VOPs (%d Is - %d Ps)", num_samples, nbI, nbP);
	}

	samp->data = NULL;
	M4_DeleteSample(&samp);

	if (erase_pl) {
		M4V_RewritePL((unsigned char **) &import->esd->decoderConfig->decoderSpecificInfo->data, &import->esd->decoderConfig->decoderSpecificInfo->dataLength, (u8) PL);
		M4_ChangeStreamDescriptor(import->dest, track, 1, import->esd);
	}
	MP4T_RecomputeBitRate(import->dest, track);

	if (is_vfr) {
		if (nbB) {
			if (is_packed) import_message(import, M4OK, "Warning: Mix of non-coded frames: packed bitstream and encoder skiped - unpredictable timing");
		} else {
			if (import->flags & M4TI_NO_FRAME_DROP) {
				if (nbNotCoded) import_message(import, M4OK, "Stream has %d N-VOPs", nbNotCoded);
			} else {
				import_message(import, M4OK, "import using Variable Frame Rate - Removed %d N-VOPs", nbNotCoded);
			}
			nbNotCoded = 0;
		}
	}

	if (nbDummy || nbNotCoded) import_message(import, M4OK, "Removed Frames: %d VFW delay frames - %d N-VOPs", nbDummy, nbNotCoded);
	M4_SetMoviePLIndication(import->dest, M4_PL_VISUAL, (u8) PL);

exit:
	if (destroy_esd) OD_DeleteDescriptor((Descriptor **) &import->esd);
	if (frame) free(frame);
	AVI_close(in);
	return e;
}




/*credits to CISCO MPEG4/IP for MP3 parsing*/
M4Err MP4T_ImportAVIAudio(M4TrackImporter *import)
{
	M4Err e;
	FILE *test;
	u32 hdr, di, track, i, tot_size, duration;
	s64 offset;
	s32 size, max_size, done;
	u16 sampleRate;
	Double dur;
	Bool is_cbr;
	u8 oti;
	M4Sample *samp;
	unsigned char *frame;
	Bool destroy_esd;
	s32 continuous;
	ESDescriptor *esd;
	unsigned char temp[4];
	avi_t *in;

	if (import->flags & M4TI_PROBE_ONLY) return M4OK;

	/*video only, ignore*/
	if (import->trackID==1) return M4OK;

	test = fopen(import->in_name, "rb");
	if (!test) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);
	fclose(test);
	in = AVI_open_input_file(import->in_name, 1);
	if (!in) return import_message(import, M4NotSupported, "Unsupported avi file");

	AVI_seek_start(in);

	e = M4OK;
	if (import->trackID)  AVI_set_audio_track(in, import->trackID-2);

	if (AVI_read_audio(in, (char *) temp, 4, &continuous) != 4) {
		AVI_close(in);
		return import_message(import, M4OK, "No audio track found");
	}
	hdr = FOUR_CHAR_INT(temp[0], temp[1], temp[2], temp[3]);
	if ((hdr &0xFFE00000) != 0xFFE00000) {
		AVI_close(in);
		return import_message(import, M4NotSupported, "Unsupported AVI audio format");
	}

	sampleRate = MP3_GetSamplingRate(hdr);
	oti = MP3_GetObjectTypeIndication(hdr);
	if (!oti || !sampleRate) {
		AVI_close(in);
		return import_message(import, M4NotSupported, "Error: invalid MPEG Audio track");
	}
		
	frame = NULL;
	destroy_esd = 0;
	esd = import->esd;

	if (!esd) {
		destroy_esd = 1;
		esd = OD_NewESDescriptor(0);
	}
	track = M4_NewTrack(import->dest, esd->ESID, M4_AudioMediaType, sampleRate);
	if (!track) goto exit;
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;

	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	esd->slConfig->timestampResolution = sampleRate;
	if (esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &esd->decoderConfig->decoderSpecificInfo);
	esd->decoderConfig->streamType = M4ST_AUDIO;
	esd->decoderConfig->objectTypeIndication = oti;
	e = M4_NewStreamDescription(import->dest, track, esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
	if (e) goto exit;

	import_message(import, M4OK, "AVI Audio import - sample rate %d - %s audio - %d channel%s", sampleRate, (oti==0x6B) ? "MPEG-1" : "MPEG-2", MP3_GetNumChannels(hdr), (MP3_GetNumChannels(hdr)>1) ? "s" : "");

	AVI_seek_start(in);
	AVI_set_audio_position(in, 0);

	i = 0;
	tot_size = max_size = 0;
	while ((size = AVI_audio_size(in, i) )>0) {
		if (max_size<size) max_size=size;
		tot_size += size;
		i++;
	}

	frame = malloc(sizeof(char) * max_size);
	AVI_seek_start(in);
	AVI_set_audio_position(in, 0);

	dur = import->duration;
	dur *= sampleRate;
	dur /= 1000;
	duration = (u32) dur;

	samp = M4_NewSample();
	done=max_size=0;
	is_cbr = 1;
	while (1) {
		if (AVI_read_audio(in, frame, 4, &continuous) != 4) break;
		offset = f64_tell(in->fdes) - 4;
		hdr = FOUR_CHAR_INT(frame[0], frame[1], frame[2], frame[3]);

		size = MP3_GetFrameSize(hdr);
		if (size>max_size) {
			frame = realloc(frame, sizeof(char) * size);
			if (max_size) is_cbr = 0;
			max_size = size;
		}
		size = 4 + AVI_read_audio(in, (char*) &frame[4], size - 4, &continuous);

		if ((import->flags & M4TI_USE_DATAREF) && !continuous) {
			import_message(import, M4IOErr, "Cannot use media references, splited input audio frame found");
			e = M4IOErr;
			goto exit;
		}
		samp->IsRAP = 1;
		samp->data = frame;
		samp->dataLength = size;
		if (import->flags & M4TI_USE_DATAREF) {
			e = M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			e = M4_AddSample(import->dest, track, di, samp);
		}

		samp->DTS += MP3_GetSamplesPerFrame(hdr);
		import_progress(import, done, tot_size);

		done += size;
		if (duration && (samp->DTS > duration) ) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	
	import_progress(import, tot_size, tot_size);

	import_message(import, M4OK, "Import done - %s bit rate MP3 detected", is_cbr ? "constant" : "variable");
	samp->data = NULL;
	M4_DeleteSample(&samp);

	MP4T_RecomputeBitRate(import->dest, track);

	M4_SetMoviePLIndication(import->dest, M4_PL_AUDIO, 0xFE);

	
exit:
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	if (frame) free(frame);
	AVI_close(in);
	return e;
}

M4Err MP4T_ImportMP4(M4TrackImporter *import)
{
	M4Err e;
	u64 offset;
	u32 track, di, trackID, track_in, i, num_samples, sampDTS, mtype, w, h, duration, sr, ch;
	u8 bps;
	char lang[4];
	const char *url, *urn;
	Bool sbr;
	M4Sample *samp;
	ESDescriptor *origin_esd;
	InitialObjectDescriptor *iod;

	if (import->flags & M4TI_PROBE_ONLY) {
		for (i=0; i<M4_GetTrackCount(import->orig); i++) {
			import->tk_info[i].track_num = M4_GetTrackID(import->orig, i+1);
			import->tk_info[i].type = M4_GetMediaType(import->orig, i+1);
			import->tk_info[i].flags = M4TI_USE_DATAREF;
			import->nb_tracks ++;
		}
		return M4OK;
	}

	trackID = import->trackID;
	if (!trackID) {
		if (M4_GetTrackCount(import->orig) != 1) return import_message(import, M4BadParam, "Several tracks in MP4 - please indicate track to import");
		trackID = M4_GetTrackID(import->orig, 1);
	}
	track_in = M4_GetTrackByID(import->orig, trackID);
	if (!track_in) return import_message(import, M4URLNotFound, "Cannot find track ID %d in file", trackID);

	origin_esd = M4_GetStreamDescriptor(import->orig, track_in, 1);

	e = M4OK;
	if (import->esd && origin_esd) {
		origin_esd->OCRESID = import->esd->OCRESID;
		/*there may be other things to import...*/
	}
	sbr = 0;
	iod = (InitialObjectDescriptor *) M4_GetRootOD(import->orig);
	if (iod && (iod->tag != InitialObjectDescriptor_Tag)) {
		OD_DeleteDescriptor((Descriptor **) &iod);
		iod = NULL;
	}
	mtype = M4_GetMediaType(import->orig, track_in);
	if (mtype==M4_VisualMediaType) {
		u8 PL = iod ? iod->visual_profileAndLevel : 0xFE;
		w = h = 0;
		M4_GetVisualEntrySize(import->orig, track_in, 1, &w, &h);
		/*for MPEG-4 visual, always check size (don't trust input file)*/
		if (origin_esd && (origin_esd->decoderConfig->objectTypeIndication==0x20)) {
			M4VDecoderSpecificInfo dsi;
			M4V_GetConfig(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
			w = dsi.width;
			h = dsi.height;
			PL = dsi.VideoPL;
		}
		M4_SetMoviePLIndication(import->dest, M4_PL_VISUAL, PL);
	}
	else if (mtype==M4_AudioMediaType) {
		u8 PL = iod ? iod->audio_profileAndLevel : 0xFE;
		bps = 16;
		sr = ch = 0;
		sbr = 0;
		M4_GetAudioEntryInfo(import->orig, track_in, 1, &sr, &ch, &bps);
		if (origin_esd && (origin_esd->decoderConfig->objectTypeIndication==0x40)) {
			M4ADecoderSpecificInfo dsi;
			M4A_GetConfig(origin_esd->decoderConfig->decoderSpecificInfo->data, origin_esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
			sr = dsi.has_sbr ? dsi.sbr_sr : dsi.base_sr;
			ch = dsi.nb_chan;
			PL = dsi.audioPL;
			sbr = dsi.has_sbr;
		}
		M4_SetMoviePLIndication(import->dest, M4_PL_AUDIO, PL);
	}
	OD_DeleteDescriptor((Descriptor **) &iod);
	
	track = M4_NewTrack(import->dest, import->esd ? import->esd->ESID : 0, M4_GetMediaType(import->orig, track_in), M4_GetMediaTimeScale(import->orig, track_in));
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	/*setup data ref*/
	urn = url = NULL;
	if (import->flags & M4TI_USE_DATAREF) {
		url = M4_GetFilename(import->orig);
		if (!M4_IsSelfContained(import->orig, track_in, 1)) {
			e = M4_GetStreamDataReference(import->orig, track_in, 1, &url, &urn);
			if (e) goto exit;
		}
	}
	if (origin_esd) {
		e = M4_NewStreamDescription(import->dest, track, origin_esd, (char *) url, (char *) urn, &di);
	} else {
		e = M4_CloneStreamDescription(import->dest, track, import->orig, track_in, 1, (char *) url, (char *) urn, &di);
	}

	if (e) goto exit;
	switch (mtype) {
	case M4_VisualMediaType:
		M4_SetVisualEntrySize(import->dest, track, di, w, h);
		import_message(import, M4OK, "IsoMedia import - track ID %d - Video (size %d x %d)", trackID, w, h);
		break;
	case M4_AudioMediaType:
	{
		M4_SetAudioDescriptionInfo(import->dest, track, di, sr, (ch>1) ? 2 : 1, bps);
		import_message(import, M4OK, "IsoMedia import - track ID %d - Audio (SR %d - %d channels)%s", trackID, sr, ch, sbr ? " - SBR AAC" : "");
	}
		break;
	default:
	{
		char szType[5], szSubType[5];
		FCC_TO_STR(mtype, szType);
		FCC_TO_STR(M4_GetMediaSubType(import->orig, track_in, di), szSubType);
		import_message(import, M4OK, "IsoMedia import - track ID %d - media type %s sub-type %s", trackID, szType, szSubType);
	}
		break;
	}
	/*copy over language*/
	lang[3] = 0;
	M4_GetMediaLanguage(import->orig, track_in, lang);
	M4_SetMediaLanguage(import->dest, track, lang);

	duration = (u32) (((Double)import->duration * M4_GetMediaTimeScale(import->orig, track_in)) / 1000);

	num_samples = M4_GetSampleCount(import->orig, track_in);
	for (i=0; i<num_samples; i++) {
		if (import->flags & M4TI_USE_DATAREF) {
			samp = M4_GetSampleInfo(import->orig, track_in, i+1, &di, &offset);
			if (!samp) {
				e = M4_GetLastError(import->orig);
				goto exit;
			}
			e = M4_AddSampleReference(import->dest, track, 1, samp, offset);
		} else {
			samp = M4_GetSample(import->orig, track_in, i+1, &di);
			e = M4_AddSample(import->dest, track, 1, samp);
		}
		sampDTS = samp->DTS;
		M4_DeleteSample(&samp);
		import_progress(import, i+1, num_samples);
		if (duration && (sampDTS > duration) ) break;
		if (import->flags & M4TI_DO_ABORT) break;
		if (e) break;
	}

	if (import->esd) {
		if (!import->esd->slConfig) {
			import->esd->slConfig = origin_esd ? origin_esd->slConfig : NULL;
			if (origin_esd) origin_esd->slConfig = NULL;
		}
		if (!import->esd->decoderConfig) {
			import->esd->decoderConfig = origin_esd ? origin_esd->decoderConfig : NULL;
			if (origin_esd) origin_esd->decoderConfig = NULL;
		}
	}		

	MP4T_RecomputeBitRate(import->dest, track);

exit:
	if (origin_esd) OD_DeleteDescriptor((Descriptor **) &origin_esd);
	return e;
}

#include "mpeg2_ps.h"

M4Err MP4T_ImportMPEG12Video(M4TrackImporter *import)
{
	M4Err e;
	mpeg2ps_t *ps;
	Double FPS;
	char *buf;
	u8 ftype;
	u32 track, di, streamID, mtype, w, h, nb_streams, buf_len, frames, ref_frame, timescale, duration, file_size, dts_inc, last_pos;
	Bool destroy_esd;
	ESDescriptor *esd;

	if (import->flags & M4TI_USE_DATAREF) 
		return import_message(import, M4NotSupported, "Cannot use data referencing with MPEG-1/2 files");

	ps = mpeg2ps_init(import->in_name);
	if (!ps) return import_message(import, M4NonCompliantBitStream, "Failed to open MPEG file %s", import->in_name);

	if (import->flags & M4TI_PROBE_ONLY) {
		u32 i, nb_v_str;
		import->nb_tracks = 0;
		nb_v_str = nb_streams = mpeg2ps_get_video_stream_count(ps);
		for (i=0; i<nb_streams; i++) {
			import->tk_info[import->nb_tracks].track_num = i+1;
			import->tk_info[import->nb_tracks].type = M4_VisualMediaType;
			import->tk_info[import->nb_tracks].flags = M4TI_OVERRIDE_FPS;
			import->nb_tracks++;
		}
		nb_streams = mpeg2ps_get_audio_stream_count(ps);
		for (i=0; i<nb_streams; i++) {
			import->tk_info[import->nb_tracks].track_num = nb_v_str + i+1;
			import->tk_info[import->nb_tracks].type = M4_AudioMediaType;
			import->nb_tracks ++;
		}
		mpeg2ps_close(ps);
		return M4OK;
	}


	streamID = 0;
	nb_streams = mpeg2ps_get_video_stream_count(ps);
	if ((nb_streams>1) && !import->trackID) {
		mpeg2ps_close(ps);
		return import_message(import, M4BadParam, "%d video tracks in MPEG file - please indicate track to import", nb_streams);
	}
	/*audio*/
	if (import->trackID>nb_streams) {
		mpeg2ps_close(ps);
		return M4OK;
	}
	if (import->trackID) streamID = import->trackID - 1;

	if (streamID>=nb_streams) {
		mpeg2ps_close(ps);
		return import_message(import, M4BadParam, "Desired video track not found in MPEG file (%d visual streams)", nb_streams);
	}
	w = mpeg2ps_get_video_stream_width(ps, streamID);
	h = mpeg2ps_get_video_stream_height(ps, streamID);
	mtype = (mpeg2ps_get_video_stream_type(ps, streamID) == MPEG_VIDEO_MPEG2) ? 0x61 : 0x6A;
	FPS = mpeg2ps_get_video_stream_framerate(ps, streamID);
	if (import->video_fps) FPS = (Double) import->video_fps;
	get_video_timing(FPS, &timescale, &dts_inc);

	duration = import->duration*timescale;
	duration /= 1000;

	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		destroy_esd = 1;
		esd = OD_NewESDescriptor(0);
	}
	track = M4_NewTrack(import->dest, esd->ESID, M4_VisualMediaType, timescale);
	e = M4_GetLastError(import->dest);
	if (!track) goto exit;
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;

	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	esd->slConfig->timestampResolution = timescale;
	if (esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &esd->decoderConfig->decoderSpecificInfo);
	esd->decoderConfig->streamType = M4ST_VISUAL;
	esd->decoderConfig->objectTypeIndication = mtype;
	e = M4_NewStreamDescription(import->dest, track, esd, NULL, NULL, &di);
	if (e) goto exit;

	import_message(import, M4OK, "%s Video import - Resolution %d x %d @ %g FPS", (mtype==0x6A) ? "MPEG-1" : "MPEG-2", w, h, FPS);
	M4_SetVisualEntrySize(import->dest, track, di, w, h);

	M4_SetCTSPackMode(import->dest, track, 1);

	file_size = (u32) mpeg2ps_get_ps_size(ps);
	last_pos = 0;
	frames = 1;
	ref_frame = 1;
	while (mpeg2ps_get_video_frame(ps, streamID, (u8 **) &buf, &buf_len, &ftype, TS_90000, NULL)) {
		M4Sample *samp;
		if ((buf[buf_len - 4] == 0) && (buf[buf_len - 3] == 0) && (buf[buf_len - 2] == 1)) buf_len -= 4;
		samp = M4_NewSample();
		samp->data = buf;
		samp->dataLength = buf_len;
		samp->DTS = dts_inc*(frames-1);
		samp->IsRAP = (ftype==1) ? 1 : 0;
		samp->CTS_Offset = 0;
		M4_AddSample(import->dest, track, di, samp);
		samp->data = NULL;
		M4_DeleteSample(&samp);

		last_pos = (u32) mpeg2ps_get_video_pos(ps, streamID);
		import_progress(import, last_pos, file_size);

		if (ftype != 3) {
			M4_SetSampleCTSOffset(import->dest, track, ref_frame, (frames-ref_frame)*dts_inc);
			ref_frame = frames;
		}
		frames++;

		if (duration && (dts_inc*(frames-1) >= duration) ) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	M4_SetCTSPackMode(import->dest, track, 0);
	if (last_pos!=file_size) import_progress(import, frames, frames);

	MP4T_RecomputeBitRate(import->dest, track);

exit:
	if (destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	mpeg2ps_close(ps);
	return e;
}

M4Err MP4T_ImportMPEG12Audio(M4TrackImporter *import)
{
	M4Err e;
	mpeg2ps_t *ps;
	u8 *buf;
	u32 track, di, streamID, mtype, sr, nb_ch, nb_streams, buf_len, frames, hdr, duration, file_size, last_pos;
	Bool destroy_esd;
	M4Sample *samp;
	ESDescriptor *esd;

	if (import->flags & M4TI_PROBE_ONLY) return M4OK;

	if (import->flags & M4TI_USE_DATAREF) 
		return import_message(import, M4NotSupported, "Cannot use data referencing with MPEG-1/2 files");

	ps = mpeg2ps_init(import->in_name);
	if (!ps) return import_message(import, M4NonCompliantBitStream, "Failed to open MPEG file %s", import->in_name);


	streamID = 0;
	nb_streams = mpeg2ps_get_audio_stream_count(ps);
	if ((nb_streams>1) && !import->trackID) {
		mpeg2ps_close(ps);
		return import_message(import, M4BadParam, "%d audio tracks in MPEG file - please indicate track to import", nb_streams);
	}

	if (import->trackID) {
		u32 nb_v = mpeg2ps_get_video_stream_count(ps);
		/*video*/
		if (import->trackID<=nb_v) {
			mpeg2ps_close(ps);
			return M4OK;
		}
		streamID = import->trackID - 1 - nb_v;
	}

	if (streamID>=nb_streams) {
		mpeg2ps_close(ps);
		return import_message(import, M4BadParam, "Desired audio track not found in MPEG file (%d audio streams)", nb_streams);
	}
	
	mtype = mpeg2ps_get_audio_stream_type(ps, streamID);
	if (mtype != MPEG_AUDIO_MPEG) {
		mpeg2ps_close(ps);
		return import_message(import, M4NotSupported, "Audio format not supported in MP4");
	}

	if (mpeg2ps_get_audio_frame(ps, streamID, &buf, &buf_len, TS_90000, NULL, NULL) == 0) {
		mpeg2ps_close(ps);
		return import_message(import, M4IOErr, "Cannot fetch audio frame from MPEG file");
	}
    
	hdr = FOUR_CHAR_INT(buf[0],buf[1],buf[2],buf[3]);
	mtype = MP3_GetObjectTypeIndication(hdr);
	sr = MP3_GetSamplingRate(hdr);
	nb_ch = MP3_GetNumChannels(hdr);

	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		destroy_esd = 1;
		esd = OD_NewESDescriptor(0);
	}
	track = M4_NewTrack(import->dest, esd->ESID, M4_AudioMediaType, sr);
	e = M4_GetLastError(import->dest);
	if (!track) goto exit;
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;

	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
	esd->slConfig->timestampResolution = sr;
	if (esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &esd->decoderConfig->decoderSpecificInfo);
	esd->decoderConfig->streamType = M4ST_AUDIO;
	esd->decoderConfig->objectTypeIndication = mtype;
	e = M4_NewStreamDescription(import->dest, track, esd, NULL, NULL, &di);
	if (e) goto exit;

	M4_SetAudioDescriptionInfo(import->dest, track, di, sr, nb_ch, 16);
	import_message(import, M4OK, "%s Audio import - sample rate %d - %d channel%s", (mtype==0x6B) ? "MPEG-1" : "MPEG-2", sr, nb_ch, (nb_ch>1) ? "s" : "");


	duration = (u32) ((Double)import->duration/1000.0 * sr);

	samp = M4_NewSample();
	samp->IsRAP = 1;
	samp->DTS = 0;

	file_size = (u32) mpeg2ps_get_ps_size(ps);
	last_pos = 0;
	frames = 0;
	do {
		samp->data = buf;
		samp->dataLength = buf_len;
		M4_AddSample(import->dest, track, di, samp);
		samp->DTS += MP3_GetSamplesPerFrame(hdr);
		last_pos = (u32) mpeg2ps_get_audio_pos(ps, streamID);
		import_progress(import, last_pos, file_size);
		frames++;
		if (duration && (samp->DTS>=duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}  while (mpeg2ps_get_audio_frame(ps, streamID, &buf, &buf_len, TS_90000, NULL, NULL));

	samp->data = NULL;
	M4_DeleteSample(&samp);
	if (last_pos!=file_size) import_progress(import, frames, frames);
	MP4T_RecomputeBitRate(import->dest, track);

exit:
	if (destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	mpeg2ps_close(ps);
	return e;
}

/*FIXME - need LARGE FILE support in NHNT - add a new version*/
M4Err MP4T_ImportNHNT(M4TrackImporter *import)
{
	M4Err e;
	Bool destroy_esd;
	u32 track, di, mtype, max_size, duration, count, w, h;
	M4Sample *samp;
	ESDescriptor *esd;
	s64 media_size, media_done, offset;
	BitStream *bs;
	FILE *nhnt, *mdia, *info;
	char *ext, szName[1000], szMedia[1000], szNhnt[1000];

	if (import->flags & M4TI_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = 0;
		import->tk_info[0].flags = M4TI_USE_DATAREF;
		return M4OK;
	}

	strcpy(szName, import->in_name);
	ext = strrchr(szName, '.');
	if (ext) ext[0] = 0;

	strcpy(szMedia, szName);
	strcat(szMedia, ".nhnt");
	nhnt = fopen(szMedia, "rb");
	if (!nhnt) return import_message(import, M4URLNotFound, "Cannot find NHNT file %s", szMedia);

	strcpy(szMedia, szName);
	strcat(szMedia, ".media");
	mdia = f64_open(szMedia, "rb");
	if (!mdia) {
		fclose(nhnt);
		return import_message(import, M4URLNotFound, "Cannot find MEDIA file %s", szMedia);
	}
	

	e = M4OK;
	destroy_esd = 0;
	esd = import->esd;
	if (!esd) {
		esd = OD_NewESDescriptor(2);
		destroy_esd = 1;
	}
	/*update stream type/oti*/
	if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
	if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);


	strcpy(szNhnt, szName);
	strcat(szNhnt, ".info");
	info = fopen(szNhnt, "rb");
	if (esd->decoderConfig->decoderSpecificInfo) OD_DeleteDescriptor((Descriptor **) &esd->decoderConfig->decoderSpecificInfo);
	if (info) {
		esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
		fseek(info, 0, SEEK_END);
		esd->decoderConfig->decoderSpecificInfo->dataLength = (u32) ftell(info);
		esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char) * esd->decoderConfig->decoderSpecificInfo->dataLength);
		fseek(info, 0, SEEK_SET);
		fread(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, 1, info);
		fclose(info);
	}

	bs = NewBitStreamFromFile(nhnt, BS_FILE_READ);
	if ((BS_ReadU8(bs)!='N') || (BS_ReadU8(bs) != 'H') || (BS_ReadU8(bs) != 'n')|| (BS_ReadU8(bs) != 't')) {
		import_message(import, M4NonCompliantBitStream, "Invalid NHNT signature");
		e = M4NonCompliantBitStream;
		goto exit;
	}
	/*version*/
	BS_ReadU8(bs);
	esd->decoderConfig->streamType = BS_ReadU8(bs);
	esd->decoderConfig->objectTypeIndication = BS_ReadU8(bs);
	BS_ReadU16(bs);
	esd->decoderConfig->bufferSizeDB = BS_ReadU24(bs);
	esd->decoderConfig->avgBitrate = BS_ReadU32(bs);
	esd->decoderConfig->maxBitrate = BS_ReadU32(bs);
	esd->slConfig->timestampResolution = BS_ReadU32(bs);
	
	w = h = 0;
	switch (esd->decoderConfig->streamType) {
	case M4ST_SCENE:
		mtype = M4_BIFSMediaType;
		/*we don't know PLs from NHNT...*/
		M4_SetMoviePLIndication(import->dest, M4_PL_SCENE, 0xFE);
		M4_SetMoviePLIndication(import->dest, M4_PL_GRAPHICS, 0xFE);
		break;
/* WARNING: Code added for Bandwidth estimation in DANAE */
	case 32:
/**/
	case M4ST_VISUAL:
		mtype = M4_VisualMediaType;
		if (esd->decoderConfig->objectTypeIndication==0x20) {
			M4VDecoderSpecificInfo dsi;
			if (!esd->decoderConfig->decoderSpecificInfo) {
				e = M4NonCompliantBitStream;
				goto exit;
			}
			e = M4V_GetConfig(esd->decoderConfig->decoderSpecificInfo->data, esd->decoderConfig->decoderSpecificInfo->dataLength, &dsi);
			if (e) goto exit;
			w = dsi.width;
			h = dsi.height;
		}
		break;
	case M4ST_AUDIO:
		mtype = M4_AudioMediaType;
		break;
	case M4ST_MPEG7:
		mtype = M4_MPEG7MediaType;
		break;
	case M4ST_IPMP:
		mtype = M4_IPMPMediaType;
		break;
	case M4ST_OCI:
		mtype = M4_OCIMediaType;
		break;
	case M4ST_MPEGJ:
		mtype = M4_MPEGJMediaType;
		break;
	/*note we cannot import OD, OCR and interaction streams from NHNT*/
	default:
		e = M4NotSupported;
		goto exit;
	}
	track = M4_NewTrack(import->dest, esd->ESID, mtype, esd->slConfig->timestampResolution);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = esd->ESID;
	e = M4_NewStreamDescription(import->dest, track, esd, (import->flags & M4TI_USE_DATAREF) ? szMedia : NULL, NULL, &di);
	if (e) goto exit;

	if (w && h) M4_SetVisualEntrySize(import->dest, track, di, w, h);

	import_message(import, M4OK, "NHNT import - Stream Type %s - ObjectTypeIndication %d", OD_GetStreamTypeName(esd->decoderConfig->streamType), esd->decoderConfig->objectTypeIndication);

	duration = import->duration*esd->slConfig->timestampResolution;
	duration /= 1000;

	samp = M4_NewSample();
	samp->data = malloc(sizeof(char) * 1024);
	max_size = 1024;
	count = 0;
	fseek(mdia, 0, SEEK_END);
	media_size = ftell(mdia);
	fseek(mdia, 0, SEEK_SET);
	media_done = 0;

	while (!feof(nhnt)) {
		samp->dataLength = BS_ReadU24(bs);
		samp->IsRAP = BS_ReadInt(bs, 1);
		BS_ReadInt(bs, 7);
		offset = BS_ReadU32(bs);
		samp->CTS_Offset = BS_ReadU32(bs);
		samp->DTS = BS_ReadU32(bs);
		samp->CTS_Offset -= samp->DTS;
		if (!count && samp->DTS) samp->DTS = 0;
		count++;

		if (import->flags & M4TI_USE_DATAREF) {
			M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			if (samp->dataLength>max_size) {
				samp->data = realloc(samp->data, sizeof(char) * samp->dataLength);
				max_size = samp->dataLength;
			}
			f64_seek(mdia, offset, SEEK_SET);
			fread( samp->data, samp->dataLength, 1, mdia); 
			M4_AddSample(import->dest, track, di, samp);
		}
		media_done += samp->dataLength;
		import_progress(import, (u32) media_done, (u32) media_size);
		if (!BS_Available(bs)) break;
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	if (media_done!=media_size) import_progress(import, (u32) media_size, (u32) media_size);
	M4_DeleteSample(&samp);	
	MP4T_RecomputeBitRate(import->dest, track);

exit:
	DeleteBitStream(bs);
	fclose(nhnt);
	fclose(mdia);
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	return e;
}



M4Err MP4T_ImportAMR_EVRC_SMV(M4TrackImporter *import)
{
	M4Err e;
	u32 track, trackID, di, max_size, duration, sample_rate, block_size, i;
	M4Sample *samp;
	char magic[20];
	Bool delete_esd;
	u32 media_size, media_done, offset, mtype, oti;
	GPPMediaConfig gpp_cfg;
	FILE *mdia;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_AudioMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_FORCE_MPEG4 | M4TI_3GPP_AGGREGATION;
		return M4OK;
	}

	mdia = fopen(import->in_name, "rb");
	if (!mdia) return import_message(import, M4URLNotFound, "Cannot find file %s", import->in_name);
	
	oti = mtype = 0;
	sample_rate = 8000;
	block_size = 160;
	fread(magic, 1, 20, mdia);
	if (!strnicmp(magic, "#!AMR\n", 6)) {
		import_message(import, M4OK, "Importing AMR Audio");
		fseek(mdia, 6, SEEK_SET);
		mtype = M4_3GP_AMR_SubType;
	}
	else if (!strnicmp(magic, "#!EVRC\n", 7)) {
		import_message(import, M4OK, "Importing EVRC Audio");
		fseek(mdia, 7, SEEK_SET);
		mtype = M4_3GP_EVRC_SubType;
		oti = 0xA0;
	}
	else if (!strnicmp(magic, "#!SMV\n", 6)) {
		import_message(import, M4OK, "Importing SMV Audio");
		fseek(mdia, 6, SEEK_SET);
		mtype = M4_3GP_SMV_SubType;
		oti = 0xA1;
	}
	else if (!strnicmp(magic, "#!AMR_MC1.0\n", 12)) {
		fclose(mdia);
		return import_message(import, M4NotSupported, "Multichannel AMR Audio Not Supported");
	}
	else if (!strnicmp(magic, "#!AMR-WB\n", 9)) {
		import_message(import, M4OK, "Importing AMR WideBand Audio");
		fseek(mdia, 9, SEEK_SET);
		mtype = M4_3GP_AMR_WB_SubType;
		sample_rate = 16000;
		block_size = 320;
	}
	else if (!strnicmp(magic, "#!AMR-WB_MC1.0\n", 15)) {
		fclose(mdia);
		return import_message(import, M4NotSupported, "Multichannel AMR WideBand Audio Not Supported");
	}
	else {
		fclose(mdia);
		return import_message(import, M4NonCompliantBitStream, "Corrupted AMR file header (expecting '#!AMR...')");
	}

	delete_esd = 0;
	trackID = 0;
	e = M4OK;
	if (import->esd) trackID = import->esd->ESID;

	track = M4_NewTrack(import->dest, trackID, M4_AudioMediaType, sample_rate);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	memset(&gpp_cfg, 0, sizeof(GPPMediaConfig));
	gpp_cfg.type = mtype;
	gpp_cfg.frames_per_sample = 1;
	if (oti && (import->flags & M4TI_FORCE_MPEG4)) {
		if (!import->esd) {
			delete_esd = 1;
			import->esd = OD_NewESDescriptor(2);
			import->esd->ESID = trackID;
		}
		import->esd->decoderConfig->streamType = M4ST_AUDIO;
		import->esd->decoderConfig->objectTypeIndication = oti;
		e = M4_NewStreamDescription(import->dest, track, import->esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
		if (e) goto exit;
		gpp_cfg.frames_per_sample = 1;
	} else {
		import->flags &= ~M4TI_FORCE_MPEG4;
		gpp_cfg.vendor = FOUR_CHAR_INT('G', 'P', 'A', 'C');
		e = M4_3GP_NewStreamConfig(import->dest, track, &gpp_cfg, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
		if (e) goto exit;
	}
	M4_SetAudioDescriptionInfo(import->dest, track, di, sample_rate, 1, 8);
	duration = import->duration * sample_rate;
	duration /= 1000;

	samp = M4_NewSample();
	samp->data = malloc(sizeof(char) * 200);
	samp->IsRAP = 1;
	max_size = 200;
	offset = ftell(mdia);
	fseek(mdia, 0, SEEK_END);
	media_size = ftell(mdia) - offset;
	fseek(mdia, offset, SEEK_SET);

	media_done = 0;

	while (!feof(mdia)) {
		u8 ft, toc;
		
		offset = ftell(mdia);
		toc = fgetc(mdia);
		switch (gpp_cfg.type) {
		case M4_3GP_AMR_SubType:
		case M4_3GP_AMR_WB_SubType:
			ft = (toc >> 3) & 0x0F;
			if (gpp_cfg.type==M4_3GP_AMR_WB_SubType) {
				samp->dataLength = amr_wb_frame_size[ft];
			} else {
				samp->dataLength = amr_nb_frame_size[ft];
			}
			samp->data[0] = toc;
			break;
		case M4_3GP_EVRC_SubType:
		case M4_3GP_SMV_SubType:
			for (i=0; i<nb_smv_evrc_rates_to_sizes; i++) {
				if (smv_evrc_rates_to_sizes[2*i]==toc) {
					/*remove rate_type byte*/
					samp->dataLength = smv_evrc_rates_to_sizes[2*i+1] - 1;
					break;
				}
			}
			samp->data[0] = toc;
			break;
		}

		if (samp->dataLength) 
			fread( samp->data + 1, samp->dataLength, 1, mdia);
		
		samp->dataLength += 1;

		if (import->flags & M4TI_USE_DATAREF) {
			M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			M4_AddSample(import->dest, track, di, samp);
		}

		samp->DTS += block_size;
		media_done += samp->dataLength;
		import_progress(import, (u32) media_done, (u32) media_size);
		if (duration && (samp->DTS > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;
	}
	M4_DeleteSample(&samp);	
	if (import->flags & M4TI_FORCE_MPEG4) MP4T_RecomputeBitRate(import->dest, track);

exit:
	if (delete_esd) OD_DeleteDescriptor((Descriptor **) &import->esd);
	fclose(mdia);
	return e;
}

/*QCP codec GUIDs*/
static const unsigned char *QCP_QCELP_GUID_1 = "\x41\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
static const unsigned char *QCP_QCELP_GUID_2 = "\x42\x6D\x7F\x5E\x15\xB1\xD0\x11\xBA\x91\x00\x80\x5F\xB4\xB9\x7E";
static const unsigned char *QCP_EVRC_GUID = "\x8D\xD4\x89\xE6\x76\x90\xB5\x46\x91\xEF\x73\x6A\x51\x00\xCE\xB4";
static const unsigned char *QCP_SMV_GUID = "\x75\x2B\x7C\x8D\x97\xA7\x46\xED\x98\x5E\xD5\x3C\x8C\xC7\x5F\x84";

M4Err MP4T_ImportQCP(M4TrackImporter *import)
{
	M4Err e;
	u32 track, trackID, di, i, nb_pck, max_size, duration, riff_size, chunk_size, major, minor, version, avg_bps, pck_size, block_size, bps, samplerate, vrat_rate_flag, size_in_packets, nb_frames;
	BitStream *bs;
	M4Sample *samp;
	unsigned char magic[12], GUID[16], name[81], fmt[162];
	u32 media_size, media_done, offset, rtable_cnt;
	Bool has_pad;
	QCPRateTable rtable[8];
	Bool delete_esd;
	GPPMediaConfig gpp_cfg;
	FILE *mdia;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_AudioMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_FORCE_MPEG4 | M4TI_3GPP_AGGREGATION;
		return M4OK;
	}

	memset(&gpp_cfg, 0, sizeof(GPPMediaConfig));
	delete_esd = 0;

	mdia = fopen(import->in_name, "rb");
	if (!mdia) return import_message(import, M4URLNotFound, "Cannot find file %s", import->in_name);
	
	bs = NewBitStreamFromFile(mdia, BS_FILE_READ);
	BS_ReadData(bs, magic, 4);
	if (strnicmp(magic, "RIFF", 4)) {
		e = import_message(import, M4NonCompliantBitStream, "Broken QCP file: RIFF header not found");
		goto exit;
	}
	riff_size = BS_ReadU32LE(bs);
	BS_ReadData(bs, fmt, 162);
	BS_Seek(bs, 8);
	BS_ReadData(bs, magic, 4);
	if (strnicmp(magic, "QLCM", 4)) {
		e = import_message(import, M4NonCompliantBitStream, "Broken QCP file: QLCM header not found");
		goto exit;
	}
	max_size = (u32) BS_GetSize(bs);
	if (max_size != riff_size+8) {
		e = import_message(import, M4NonCompliantBitStream, "Broken QCP file: Expecting RIFF-Size %d got %d", max_size-8, riff_size);
		goto exit;
	}
	/*fmt*/
	BS_ReadData(bs, magic, 4);
	if (strnicmp(magic, "fmt ", 4)) {
		e = import_message(import, M4NonCompliantBitStream, "Broken QCP file: FMT not found");
		goto exit;
	}
	chunk_size = BS_ReadU32LE(bs);
	has_pad = (chunk_size%2) ? 1 : 0;
	major = BS_ReadU8(bs);
	minor = BS_ReadU8(bs);
	chunk_size -= 2;
	/*codec info*/
	BS_ReadData(bs, GUID, 16);
	version = BS_ReadU16LE(bs);
	chunk_size -= 18;
	BS_ReadData(bs, name, 80);
	name[80]=0;
	chunk_size -= 80;
	avg_bps = BS_ReadU16LE(bs);
	pck_size = BS_ReadU16LE(bs);
	block_size = BS_ReadU16LE(bs);
	samplerate = BS_ReadU16LE(bs);
	bps = BS_ReadU16LE(bs);
	rtable_cnt = BS_ReadU32LE(bs);
	chunk_size -= 14;
	/*skip var rate*/
	for (i=0; i<8; i++) {
		rtable[i].pck_size = BS_ReadU8(bs);
		rtable[i].rate_idx = BS_ReadU8(bs);
	}
	chunk_size -= 16;
	BS_SkipBytes(bs, 5*4);/*reserved*/
	chunk_size -= 20;
	BS_SkipBytes(bs, chunk_size);
	if (has_pad) BS_ReadU8(bs);

	if (!strncmp(GUID, QCP_QCELP_GUID_1, 16) || !strncmp(GUID, QCP_QCELP_GUID_2, 16)) {
		gpp_cfg.type = M4_3GP_QCELP_SubType;
		strcpy(name, "QCELP-13K");
	} else if (!strncmp(GUID, QCP_EVRC_GUID, 16)) {
		gpp_cfg.type = M4_3GP_EVRC_SubType;
		strcpy(name, "EVRC");
	} else if (!strncmp(GUID, QCP_SMV_GUID, 16)) {
		gpp_cfg.type = M4_3GP_SMV_SubType;
		strcpy(name, "SMV");
	} else {
		gpp_cfg.type = 0;
	}
	/*vrat*/
	BS_ReadData(bs, magic, 4);
	if (strnicmp(magic, "vrat", 4)) {
		e = import_message(import, M4NonCompliantBitStream, "Broken QCP file: VRAT not found");
		goto exit;
	}
	chunk_size = BS_ReadU32LE(bs);
	has_pad = (chunk_size%2) ? 1 : 0;
	vrat_rate_flag = BS_ReadU32LE(bs);
	size_in_packets = BS_ReadU32LE(bs);
	chunk_size -= 8;
	BS_SkipBytes(bs, chunk_size);
	if (has_pad) BS_ReadU8(bs);

	if (!gpp_cfg.type) {
		e = import_message(import, M4NotSupported, "Unknown QCP file codec %s", name);
		goto exit;
	}

	import_message(import, M4OK, "Importing %s Audio - SampleRate %d", name, samplerate);

	trackID = 0;
	e = M4OK;
	if (import->esd) trackID = import->esd->ESID;

	track = M4_NewTrack(import->dest, trackID, M4_AudioMediaType, samplerate);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	if (import->flags & M4TI_FORCE_MPEG4) {
		if (!import->esd) {
			import->esd = OD_NewESDescriptor(2);
			delete_esd = 1;
		}
		if (!import->esd->decoderConfig) import->esd->decoderConfig = (DecoderConfigDescriptor*)OD_NewDescriptor(DecoderConfigDescriptor_Tag);
		import->esd->decoderConfig->streamType = 0x05;
		switch (gpp_cfg.type) {
		case M4_3GP_QCELP_SubType:
			import->esd->decoderConfig->objectTypeIndication = 0xE1;
			/*DSI is fmt*/
			if (!import->esd->decoderConfig->decoderSpecificInfo) import->esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
			if (import->esd->decoderConfig->decoderSpecificInfo->data) free(import->esd->decoderConfig->decoderSpecificInfo->data);
			import->esd->decoderConfig->decoderSpecificInfo->dataLength = 162;
			import->esd->decoderConfig->decoderSpecificInfo->data = malloc(sizeof(char)*162);
			memcpy(import->esd->decoderConfig->decoderSpecificInfo->data, fmt, 162);
			break;
		case M4_3GP_EVRC_SubType:
			import->esd->decoderConfig->objectTypeIndication = 0xA0;
			break;
		case M4_3GP_SMV_SubType:
			import->esd->decoderConfig->objectTypeIndication = 0xA1;
			break;
		}
		e = M4_NewStreamDescription(import->dest, track, import->esd, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
		if (e) goto exit;
	} else {
		if (import->frames_per_sample<=1) import->frames_per_sample=1;
		else if (import->frames_per_sample>15) import->frames_per_sample=15;
		gpp_cfg.frames_per_sample = import->frames_per_sample;
		e = M4_3GP_NewStreamConfig(import->dest, track, &gpp_cfg, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
		if (e) goto exit;
	}
	M4_SetAudioDescriptionInfo(import->dest, track, di, samplerate, 1, (u8) bps);

	duration = import->duration * samplerate;
	duration /= 1000;

	samp = M4_NewSample();
	samp->data = malloc(sizeof(char) * 200);
	samp->IsRAP = 1;
	max_size = 200;
	offset = ftell(mdia);
	fseek(mdia, 0, SEEK_END);
	media_size = ftell(mdia) - offset;
	fseek(mdia, offset, SEEK_SET);

	nb_pck = 0;
	media_done = 0;
	nb_frames = 0;
	while (BS_Available(bs)) {
		BS_ReadData(bs, magic, 4);
		chunk_size = BS_ReadU32LE(bs);
		has_pad = (chunk_size%2) ? 1 : 0;
		/*process chunk by chunk*/
		if (!strnicmp(magic, "data", 4)) {

			while (chunk_size) {
				u32 idx = 0;
				u32 size = 0;

				offset = (u32) BS_GetPosition(bs);
				/*get frame rate idx*/
				if (vrat_rate_flag) {
					idx = BS_ReadU8(bs);
					chunk_size-=1;
					for (i=0; i<rtable_cnt; i++) {
						if (rtable[i].rate_idx==idx) {size = rtable[i].pck_size; break;}
					}
					samp->dataLength = size+1;
				} else {
					size = pck_size;
					samp->dataLength = size;
				}
				if (max_size<samp->dataLength) {
					samp->data = realloc(samp->data, sizeof(char)*samp->dataLength);
					max_size=samp->dataLength;
				}
				if (import->flags & M4TI_USE_DATAREF) {
					M4_AddSampleReference(import->dest, track, di, samp, offset);
					BS_SkipBytes(bs, size);
				} else {
					if (vrat_rate_flag) {
						samp->data[0] = idx;
						BS_ReadData(bs, &samp->data[1], size);
					} else {
						BS_ReadData(bs, samp->data, size);
					}
					if (!nb_frames) {
						M4_AddSample(import->dest, track, di, samp);
					} else {
						M4_AppendSampleData(import->dest, track, samp->data, samp->dataLength);
					}
					nb_frames++;
					if (nb_frames==import->frames_per_sample) nb_frames=0;
				}
				chunk_size -= size;
				samp->DTS += block_size;
				if (size_in_packets) {
					import_progress(import, (u32) nb_pck, (u32) size_in_packets);
				} else {
					import_progress(import, (u32) media_done, (u32) media_size);
				}
				if (nb_pck==1504) 
					nb_pck=1504;

				nb_pck++;
				media_done += samp->dataLength;
				if (duration && (samp->DTS > duration)) break;
				if (import->flags & M4TI_DO_ABORT) break;
			}
		} else if (!strnicmp(magic, "labl", 4)) {
		} else if (!strnicmp(magic, "offs", 4)) {
		} else if (!strnicmp(magic, "cnfg", 4)) {
		} else if (!strnicmp(magic, "text", 4)) {
		}
		BS_SkipBytes(bs, chunk_size);
		if (has_pad) BS_ReadU8(bs);
	}
	M4_DeleteSample(&samp);	
	M4_SetMovieVersionInfo(import->dest, GPP2_V1_File, 65536);
	if (import->flags & M4TI_FORCE_MPEG4) MP4T_RecomputeBitRate(import->dest, track);
	import_progress(import, size_in_packets, size_in_packets);

exit:
	if (delete_esd && import->esd) OD_DeleteDescriptor((Descriptor **)&import->esd);
	DeleteBitStream(bs);
	fclose(mdia);
	return e;
}

/*read that amount of data at each IO access rather than fetching byte by byte...*/
Bool H263_IsStartCode(BitStream *bs)
{
	u32 c;
	c = BS_PeekBits(bs, 22, 0);
	if (c==0x20) return 1;
	return 0;
}

#define H263_CACHE_SIZE	4096
u32 H263_NextStartCode(BitStream *bs)
{
	u32 v, bpos;
	unsigned char h263_cache[H263_CACHE_SIZE];
	u64 end, cache_start, load_size;
	u64 start = BS_GetPosition(bs);
	
	/*skip 16b header*/
	BS_ReadU16(bs);
	bpos = 0;
	load_size = 0;
	cache_start = 0;
	end = 0;
	v = 0xffffffff;
	while (!end) {
		/*refill cache*/
		if (bpos == (u32) load_size) {
			if (!BS_Available(bs)) break;
			load_size = BS_Available(bs);
			if (load_size>H263_CACHE_SIZE) load_size=H263_CACHE_SIZE;
			bpos = 0;
			cache_start = BS_GetPosition(bs);
			BS_ReadData(bs, h263_cache, (u32) load_size);
		}
		v = (v<<8) | h263_cache[bpos];
		bpos++;
        if ((v >> (32-22)) == 0x20) end = cache_start+bpos-4;
	}
	BS_Seek(bs, start);
	if (!end) end = BS_GetSize(bs);
	return (u32) (end-start);
}
static void h263_get_pic_size(BitStream *bs, u32 fmt, u32 *w, u32 *h)
{
	switch (fmt) {
	case 1: *w = 128; *h = 96; break;
    case 2: *w = 176; *h = 144; break;
    case 3: *w = 352; *h = 288; break;
    case 4: *w = 704; *h = 576; break;
    case 5: *w = 1409; *h = 1152 ; break;
    default: *w = *h = 0; break;
    }
}

M4Err MP4T_ImportH263(M4TrackImporter *import)
{
	M4Err e;
	u32 track, trackID, di, max_size, timescale, duration, w, h, fmt, nb_samp, dts_inc;
	u64 offset;
	M4Sample *samp;
	char *samp_data;
	u32 media_size, media_done;
	GPPMediaConfig gpp_cfg;
	Double FPS;
	FILE *mdia;
	BitStream *bs;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_VisualMediaType;
		import->tk_info[0].flags = M4TI_USE_DATAREF | M4TI_OVERRIDE_FPS;
		return M4OK;
	}

	mdia = fopen(import->in_name, "rb");
	if (!mdia) return import_message(import, M4URLNotFound, "Cannot find file %s", import->in_name);

	bs = NewBitStreamFromFile(mdia, BS_FILE_READ);
	if (!H263_IsStartCode(bs)) {
		e = import_message(import, M4NonCompliantBitStream, "Cannot find H263 Picture Start Code");
		goto exit;
	}
	FPS = (Double) import->video_fps;
	/*for H263 we use 15 fps by default!!*/
	if (!FPS) FPS = 15;
	get_video_timing(FPS, &timescale, &dts_inc);

	/*parse header*/
	BS_ReadInt(bs, 22);
	BS_ReadU8(bs);
	/*spare+0+split_screen_indicator+document_camera_indicator+freeze_picture_release*/
	BS_ReadInt(bs, 5);

	fmt = BS_ReadInt(bs, 3);
	h263_get_pic_size(bs, fmt, &w, &h);
	if (!w || !h) {
		e = import_message(import, M4NotSupported, "Unsupported H263 frame header");
		goto exit;
	}
	trackID = 0;
	e = M4OK;
	if (import->esd) trackID = import->esd->ESID;

	track = M4_NewTrack(import->dest, trackID, M4_VisualMediaType, timescale);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	memset(&gpp_cfg, 0, sizeof(GPPMediaConfig));
	gpp_cfg.type = M4_3GP_H263_SubType;
	gpp_cfg.vendor = FOUR_CHAR_INT('G','P','A','C');
	gpp_cfg.H263_level = 1;
	gpp_cfg.H263_level = 1;
	e = M4_3GP_NewStreamConfig(import->dest, track, &gpp_cfg, (import->flags & M4TI_USE_DATAREF) ? import->in_name : NULL, NULL, &di);
	if (e) goto exit;
	M4_SetVisualEntrySize(import->dest, track, di, w, h);
	import_message(import, M4OK, "Importing H263 video - %d x %d @ %02.4f", w, h, FPS);

	samp = M4_NewSample();

	duration = (u32) ( ((Double)import->duration) * timescale / 1000.0);
	media_size = (u32) BS_GetSize(bs);
	nb_samp = media_done = 0;

	max_size = 4096;
	samp_data = malloc(sizeof(char)*max_size);
	BS_Seek(bs, 0);
	offset = 0;
	while (BS_Available(bs)) {
		samp->dataLength = H263_NextStartCode(bs);
		if (samp->dataLength>max_size) {
			max_size = samp->dataLength;
			samp_data = realloc(samp_data, sizeof(char)*max_size);
		}
		BS_ReadData(bs, samp_data, samp->dataLength);
		/*we ignore pict number and import at const FPS*/
		samp->IsRAP = (samp_data[4]&0x02) ? 0 : 1;
		samp->data = samp_data;
		if (import->flags & M4TI_USE_DATAREF) {
			M4_AddSampleReference(import->dest, track, di, samp, offset);
		} else {
			M4_AddSample(import->dest, track, di, samp);
		}
		samp->data = NULL;
		samp->DTS += dts_inc;
		nb_samp ++;
		offset += samp->dataLength;
		import_progress(import, media_done, media_size);
		media_done += samp->dataLength;

		if ((duration && (samp->DTS > duration) ) || (import->flags & M4TI_DO_ABORT)) {
			break;
		}
	}
	free(samp_data);
	M4_DeleteSample(&samp);
	import_progress(import, nb_samp, nb_samp);
	M4_ModifyAlternateBrand(import->dest, FOUR_CHAR_INT('3','g','g','6'), 1);
	M4_ModifyAlternateBrand(import->dest, FOUR_CHAR_INT('3','g','g','5'), 1);

exit:
	DeleteBitStream(bs);
	fclose(mdia);
	return e;
}
static void avc_rewrite_samples(M4File *file, u32 track, u32 prev_size, u32 new_size)
{
	u32 i, count, di, remain, msize;
	char *buffer;

	msize = 4096;
	buffer = malloc(sizeof(char)*msize);
	count = M4_GetSampleCount(file, track);
	for (i=0; i<count;i++) {
		M4Sample *samp = M4_GetSample(file, track, i+1, &di);
		BitStream *oldbs = NewBitStream(samp->data, samp->dataLength, BS_READ);
		BitStream *newbs = NewBitStream(NULL, 0, BS_WRITE);
		remain = samp->dataLength;
		while (remain) {
			u32 size = BS_ReadInt(oldbs, prev_size);
			BS_WriteInt(newbs, size, new_size);
			remain -= prev_size/8;
			if (size>msize) {
				msize = size;
				buffer = realloc(buffer, sizeof(char)*msize);
			}
			BS_ReadData(oldbs, buffer, size);
			BS_WriteData(newbs, buffer, size);
			remain -= size;
		}
		DeleteBitStream(oldbs);
		free(samp->data);
		samp->data = NULL;
		samp->dataLength = 0;
		BS_GetContent(newbs, (unsigned char **) &samp->data, &samp->dataLength);
		DeleteBitStream(newbs);
		M4_UpdateSample(file, track, i+1, samp, 1);
		M4_DeleteSample(&samp);
	}
	free(buffer);
}

M4Err MP4T_ImportH264(M4TrackImporter *import)
{
	u64 nal_start, nal_end;
	u32 nal_size, track, trackID, di, cur_samp, tot_size, nb_i, nb_idr, nb_p, nb_b, nb_sp, nb_si, nb_sei, max_w, max_h, duration, max_delay, max_total_delay;
	s32 idx;
	u8 nal_type;
	M4Err e;
	FILE *mdia;
	AVCState avc;
	AVCConfigSlot *slc;
	AVCConfig *avccfg;
	BitStream *bs;
	BitStream *sample_data;
	Bool flush_sample, sample_is_rap, first_nal, slice_is_ref, has_cts_offset;
	u32 b_frames, ref_frame, pred_frame, timescale, copy_size, size_length, dts_inc;
	s32 last_poc, max_last_poc, max_last_b_poc, poc_diff, prev_last_poc, min_poc, poc_shift;
	Double FPS;
	char *buffer;
	u32 max_size = 4096;

	if (import->flags & M4TI_PROBE_ONLY) {
		import->nb_tracks = 1;
		import->tk_info[0].track_num = 1;
		import->tk_info[0].type = M4_VisualMediaType;
		import->tk_info[0].flags = M4TI_OVERRIDE_FPS;
		return M4OK;
	}

	mdia = fopen(import->in_name, "rb");
	if (!mdia) return import_message(import, M4URLNotFound, "Cannot find file %s", import->in_name);
	
	FPS = (Double) import->video_fps;
	if (!FPS) FPS = M4TI_DEFAULT_FPS;
	get_video_timing(FPS, &timescale, &dts_inc);

	memset(&avc, 0, sizeof(AVCState));
	avccfg = AVC_NewConfig();
	buffer = malloc(sizeof(char) * max_size);
	sample_data = NULL;

	bs = NewBitStreamFromFile(mdia, BS_FILE_READ);
	if (!AVC_IsStartCode(bs)) {
		e = import_message(import, M4NonCompliantBitStream, "Cannot find H264 start code");
		goto exit;
	}

	/*if import in edit mode, use smallest NAL size and adjust on the fly*/
	if (M4_GetOpenMode(import->dest)!=M4_OPEN_WRITE) size_length = 8;
	else size_length = 32;
	
	trackID = 0;
	e = M4OK;
	if (import->esd) trackID = import->esd->ESID;

	track = M4_NewTrack(import->dest, trackID, M4_VisualMediaType, timescale);
	if (!track) {
		e = M4_GetLastError(import->dest);
		goto exit;
	}
	M4_SetTrackEnabled(import->dest, track, 1);
	if (import->esd && !import->esd->ESID) import->esd->ESID = M4_GetTrackID(import->dest, track);
	import->final_trackID = M4_GetTrackID(import->dest, track);

	e = M4_AVC_NewStreamConfig(import->dest, track, avccfg, NULL, NULL, &di);
	if (e) goto exit;

	sample_data = NULL;
	sample_is_rap = 0;
	cur_samp = 0;
	tot_size = (u32) BS_GetSize(bs);
	nal_start = BS_GetPosition(bs);
	duration = (u32) ( ((Double)import->duration) * timescale / 1000.0);
	
	nb_i = nb_idr = nb_p = nb_b = nb_sp = nb_si = nb_sei = 0;
	max_w = max_h = 0;
	first_nal = 1;
	b_frames = ref_frame = pred_frame = 0;
	last_poc = max_last_poc = max_last_b_poc = prev_last_poc = 0;
	max_total_delay = max_delay = 0;

	M4_SetCTSPackMode(import->dest, track, 1);
	has_cts_offset = 0;
	poc_diff = 0;
	min_poc = 0;
	poc_shift = 0;

	while (BS_Available(bs)) {
		u8 nal_hdr;
		nal_size = AVC_NextStartCode(bs);

		if (nal_size>max_size) {
			buffer = realloc(buffer, sizeof(char)*nal_size);
			max_size = nal_size;
		}
		BS_ReadData(bs, buffer, nal_size);
		BS_Seek(bs, nal_start);

		nal_hdr = BS_ReadU8(bs);
		nal_type = nal_hdr & 0x1F;

		copy_size = flush_sample = 0;
		switch (AVC_ParseNALU(bs, nal_hdr, &avc)) {
		case 1:
			flush_sample = 1;
			break;
		case -1:
			e = import_message(import, M4NonCompliantBitStream, "Error parsing NAL unit");
			goto exit;
		default:
			break;
		}

		if (AVC_NALUIsSlice(nal_type)) {
			copy_size = nal_size;
			switch (avc.s_info.slice_type) {
			case AVC_TYPE_P: case AVC_TYPE2_P: nb_p++; break;
			case AVC_TYPE_I: case AVC_TYPE2_I: nb_i++; break;
			case AVC_TYPE_B: case AVC_TYPE2_B: nb_b++; break;
			case AVC_TYPE_SP: case AVC_TYPE2_SP: nb_sp++; break;
			case AVC_TYPE_SI: case AVC_TYPE2_SI: nb_si++; break;
			}
		} else {
			switch (nal_type) {
			case AVC_NALU_SEQ_PARAM:
				idx = AVC_ReadSeqInfo(bs, &avc);
				if (idx<0) {
					e = import_message(import, M4NonCompliantBitStream, "Error parsing SeqInfo");
					goto exit;
				}
				if (avc.sps[idx].status==1) {
					avc.sps[idx].status = 2;
					avccfg->configurationVersion = 1;
					avccfg->profile_compatibility = avc.sps[idx].prof_compat;
					avccfg->AVCProfileIndication = avc.sps[idx].profile_idc;
					avccfg->AVCLevelIndication = avc.sps[idx].level_idc;
					slc = malloc(sizeof(AVCConfigSlot));
					slc->size = nal_size;
					slc->data = malloc(sizeof(char)*slc->size);
					memcpy(slc->data, buffer, sizeof(char)*slc->size);
					ChainAddEntry(avccfg->sequenceParameterSets, slc);
					if (!idx) {
						import_message(import, M4OK, "AVC-H264 import - frame size %d x %d at %.3f FPS", avc.sps[idx].width, avc.sps[idx].height, FPS);
					}
					if ((max_w <= avc.sps[idx].width) && (max_h <= avc.sps[idx].height)) {
						max_w = avc.sps[idx].width;
						max_h = avc.sps[idx].height;
					}
				}
				break;
			case AVC_NALU_PIC_PARAM:
				idx = AVC_ReadPictParamSet(bs, &avc);
				if (idx<0) {
					e = import_message(import, M4NonCompliantBitStream, "Error parsing Picture Param");
					goto exit;
				}
				if (avc.pps[idx].status==1) {
					avc.pps[idx].status = 2;
					slc = malloc(sizeof(AVCConfigSlot));
					slc->size = nal_size;
					slc->data = malloc(sizeof(char)*slc->size);
					memcpy(slc->data, buffer, sizeof(char)*slc->size);
					ChainAddEntry(avccfg->pictureParameterSets, slc);
				}
				break;
			case AVC_NALU_SEI:
				copy_size = AVC_ReformatSEI_NALU(buffer, nal_size, &avc);
				if (copy_size) nb_sei++;
				break;
			/*remove*/
			case AVC_NALU_ACCESS_UNIT:
			case AVC_NALU_FILLER_DATA:
			case AVC_NALU_END_OF_SEQ:
			case AVC_NALU_END_OF_STREAM:
				break;
			default:
				import_message(import, M4OK, "WARNING: NAL Unit type %d not handled - adding", nal_type);
				copy_size = nal_size;
				break;
			}
		}
		if (!nal_size) break;

		if (flush_sample && sample_data) {
			M4Sample *samp = M4_NewSample();
			samp->DTS = dts_inc*cur_samp;
			samp->IsRAP = sample_is_rap;
			BS_GetContent(sample_data, (unsigned char **) &samp->data, &samp->dataLength);
			DeleteBitStream(sample_data);
			sample_data = NULL;
			/*CTS recomuting is much trickier than with MPEG-4 ASP due to b-slice used as references - we therefore 
			store the poc as the CTS offset and update the whole table at the end*/
			samp->CTS_Offset = last_poc - poc_shift;
			assert(samp->CTS_Offset>=0);
			M4_AddSample(import->dest, track, di, samp);
			M4_DeleteSample(&samp);
			cur_samp++;
			import_progress(import, (u32) nal_start, tot_size);
			first_nal = 1;

			if (min_poc > last_poc) 
				min_poc = last_poc;
		}

		if (copy_size) {
			if ((size_length<32) && ( (u32) (1<<size_length)-1 < copy_size)) {
				u32 diff_size = 8;
				while ((size_length<32) && ( (u32) (1<<(size_length+diff_size))-1 < copy_size)) diff_size+=8;

				import_message(import, M4OK, "Adjusting AVC SizeLength to %d bits", size_length+diff_size);
				avc_rewrite_samples(import->dest, track, size_length, size_length+diff_size);
				
				/*rewrite current sample*/
				if (sample_data) {
					char *sd;
					u32 sd_l;
					BitStream *prev_sd;
					BS_GetContent(sample_data, (unsigned char **) &sd, &sd_l);
					DeleteBitStream(sample_data);
					sample_data = NewBitStream(NULL, 0, BS_WRITE);
					prev_sd = NewBitStream(sd, sd_l, BS_READ);
					while (BS_Available(prev_sd)) {
						char *buf;
						u32 s = BS_ReadInt(prev_sd, size_length);
						BS_WriteInt(sample_data, s, size_length+diff_size);
						buf = malloc(sizeof(char)*s);
						BS_ReadData(prev_sd, buf, s);
						BS_WriteData(sample_data, buf, s);
						free(buf);
					}
					DeleteBitStream(prev_sd);
					free(sd);
				}
				size_length+=diff_size;

			}
			if (!sample_data) sample_data = NewBitStream(NULL, 0, BS_WRITE);
			BS_WriteInt(sample_data, copy_size, size_length);
			BS_WriteData(sample_data, buffer, copy_size);
			if (AVC_NALUIsSlice(nal_type)) {
				if (first_nal) {
					first_nal = 0;
					/*we only indicate TRUE IDRs for sync samples (cf AVC file format spec).
					SEI recovery should be used to build sampleToGroup & RollRecovery tables*/
					avc.sei.recovery_point.valid = 0;

					sample_is_rap = AVC_SliceIsIDR(&avc);
				}
				slice_is_ref = (avc.s_info.nal_unit_type==AVC_NALU_IDR_SLICE);
				if (slice_is_ref) nb_idr++;

				if (avc.s_info.poc<poc_shift) {
					u32 j;
					for (j=ref_frame; j<=cur_samp; j++) {
						M4Sample *samp = M4_GetSampleInfo(import->dest, track, j, NULL, NULL);
						samp->CTS_Offset += poc_shift;
						samp->CTS_Offset -= avc.s_info.poc;
						M4_SetSampleCTSOffset(import->dest, track, j, samp->CTS_Offset);
						M4_DeleteSample(&samp);
					}
					poc_shift = avc.s_info.poc;
				}

				/*if #pics, compute smallest POC increase*/
				if (avc.s_info.poc != last_poc) {
					if (!poc_diff || (poc_diff > abs(avc.s_info.poc-last_poc))) 
						poc_diff = abs(avc.s_info.poc-last_poc);
					last_poc = avc.s_info.poc;
				}
				/*ref slice, reset poc*/
				if (slice_is_ref) {
					ref_frame = cur_samp+1;
					max_last_poc = last_poc = max_last_b_poc = 0;
					pred_frame = 0;
					b_frames = 0;
					max_delay = 0;
					poc_shift = 0;
				}
				/*strictly less - this is a new P slice*/
				else if (max_last_poc<last_poc) {
					max_delay = 0;
					max_last_b_poc = 0;
					prev_last_poc = max_last_poc;
					max_last_poc = last_poc;
					b_frames = 0;
					max_delay = 0;
				}
				/*stricly greater*/
				else if (max_last_poc>last_poc) {
					/*need to store TS offsets*/
					has_cts_offset = 1;
					switch (avc.s_info.slice_type) {
					case AVC_TYPE_B: 
					case AVC_TYPE2_B:
						if (!max_last_b_poc) {
							b_frames ++;
							max_last_b_poc = last_poc;
						}
						/*we had a B-slice reference*/
						else if (last_poc<max_last_b_poc) {
							b_frames ++;
							if (max_delay<b_frames) {
								max_delay = b_frames;
								if (max_total_delay<max_delay) max_total_delay = max_delay;
							}
							b_frames = 0;
						}
						/*if same poc than last max, this is a B-slice*/

						max_last_b_poc = last_poc;
						break;
					}
				}
			}
		}

		BS_Align(bs);
		nal_end = BS_GetPosition(bs);
		if (nal_end != nal_start + nal_size) BS_Seek(bs, nal_start + nal_size);

		if (!BS_Available(bs)) break;
		if (duration && (dts_inc*cur_samp > duration)) break;
		if (import->flags & M4TI_DO_ABORT) break;

		/*consume next start code*/
		nal_start = AVC_IsStartCode(bs);
		assert(nal_start);
		nal_start = BS_GetPosition(bs);
	}

	/*final flush*/
	if (sample_data) {
		M4Sample *samp = M4_NewSample();
		samp->DTS = dts_inc*cur_samp;
		samp->IsRAP = sample_is_rap;
		samp->CTS_Offset = last_poc - poc_shift;
		BS_GetContent(sample_data, (unsigned char **) &samp->data, &samp->dataLength);
		DeleteBitStream(sample_data);
		sample_data = NULL;
		M4_AddSample(import->dest, track, di, samp);
		M4_DeleteSample(&samp);
		import_progress(import, (u32) cur_samp, cur_samp+1);
		cur_samp++;
	}

	/*recompute all CTS offsets*/
	if (has_cts_offset) {
		u32 i, last_dts, max_cts, last_cts_samp;
		if (!poc_diff) poc_diff = 1;
		/*no b-frame references, no need to cope with negative poc*/
		if (!max_total_delay) {
			min_poc=0;
			max_total_delay = 1;
		}
		cur_samp = M4_GetSampleCount(import->dest, track);
		min_poc *= -1;
		last_dts = 0;
		max_cts = 0;
		last_cts_samp = 0;

		for (i=0; i<cur_samp; i++) {
			/*not using descIdx and data_offset will only fecth DTS, CTS and RAP which is all we need*/
			M4Sample *samp = M4_GetSampleInfo(import->dest, track, i+1, NULL, NULL);
			/*poc re-init (RAP and POC to 0, otherwise that's SEI recovery), update base DTS*/
			if (samp->IsRAP /*&& !samp->CTS_Offset*/) last_dts = samp->DTS;

			/*CTS offset is frame POC (refers to last IDR)*/
			samp->CTS_Offset = (min_poc + (s32) samp->CTS_Offset) * dts_inc/poc_diff + max_total_delay*dts_inc + last_dts - samp->DTS;

			if (max_cts < samp->DTS + samp->CTS_Offset) {
				max_cts = samp->DTS + samp->CTS_Offset;
				last_cts_samp = i;
			}
			/*this should never happen, however some streams seem to do weird POC increases (cf sorenson streams, last 2 frames),
			this should hopefully take care of some bugs and ensure proper CTS...*/
			if ((s32)samp->CTS_Offset<0) {
				u32 j, k;
				samp->CTS_Offset = 0;
				M4_SetSampleCTSOffset(import->dest, track, i+1, samp->CTS_Offset);
				for (j=last_cts_samp; j<i; j++) {
					M4Sample *asamp = M4_GetSampleInfo(import->dest, track, j+1, NULL, NULL);
					for (k=j+1; k<=i; k++) {
						M4Sample *bsamp = M4_GetSampleInfo(import->dest, track, k+1, NULL, NULL);
						if (asamp->CTS_Offset+asamp->DTS==bsamp->CTS_Offset+bsamp->DTS) {
							max_cts += dts_inc;
							bsamp->CTS_Offset = max_cts - bsamp->DTS;
							M4_SetSampleCTSOffset(import->dest, track, k+1, bsamp->CTS_Offset);
						}
						M4_DeleteSample(&bsamp);
					}
					M4_DeleteSample(&asamp);
				}
				max_cts = samp->DTS + samp->CTS_Offset;
			} else {
				M4_SetSampleCTSOffset(import->dest, track, i+1, samp->CTS_Offset);
			}
			M4_DeleteSample(&samp);
		}
		/*and repack table*/
		M4_SetCTSPackMode(import->dest, track, 0);
	} else {
		M4_DeleteCTSTable(import->dest, track);
	}

	import_progress(import, (u32) cur_samp, cur_samp);

	M4_SetVisualEntrySize(import->dest, track, di, max_w, max_h);
	avccfg->nal_unit_size = size_length/8;
	M4_AVC_UpdateStreamConfig(import->dest, track, 1, avccfg);
	MP4T_RecomputeBitRate(import->dest, track);

	M4_SetMoviePLIndication(import->dest, M4_PL_VISUAL, 0x15);
	M4_ModifyAlternateBrand(import->dest, H264_AVC_File, 1);

	if (nb_sp || nb_si) {
		import_message(import, M4OK, "Import results: %d samples - Slices: %d I %d P %d B %d SP %d SI - %d SEI - %d IDR", 
			cur_samp, nb_i, nb_p, nb_b, nb_sp, nb_si, nb_sei, nb_idr);
	} else {
		import_message(import, M4OK, "Import results: %d samples - Slices: %d I %d P %d B - %d SEI - %d IDR", 
			cur_samp, nb_i, nb_p, nb_b, nb_sei, nb_idr);
	}

	if (max_total_delay>1) {
		import_message(import, M4OK, "\tStream uses B-slice references - max frame delay %d", max_total_delay);
	}

	/*rewrite ESD*/
	if (import->esd) {
		if (!import->esd->slConfig) import->esd->slConfig = (SLConfigDescriptor*) OD_NewDescriptor(SLConfigDescriptor_Tag);
		import->esd->slConfig->predefined = 2;
		import->esd->slConfig->timestampResolution = timescale;
		if (import->esd->decoderConfig) OD_DeleteDescriptor((Descriptor **)&import->esd->decoderConfig);
		import->esd->decoderConfig = M4_GetDecoderInformation(import->dest, track, 1);
		M4_ChangeStreamDescriptor(import->dest, track, 1, import->esd);
	}

exit:
	if (sample_data) DeleteBitStream(sample_data);
	AVC_DeleteConfig(avccfg);
	free(buffer);
	DeleteBitStream(bs);
	fclose(mdia);
	return e;
}
#define OGG_BUFFER_SIZE 4096

Bool OGG_ReadPage(FILE *f_in, ogg_sync_state *oy, ogg_page *oggpage)
{
	if (feof(f_in)) return 0;
	while (ogg_sync_pageout(oy, oggpage ) != 1 ) {
		char *buffer = ogg_sync_buffer(oy, OGG_BUFFER_SIZE);
		u32 bytes = fread(buffer, 1, OGG_BUFFER_SIZE, f_in);
		ogg_sync_wrote(oy, bytes);
		if (feof(f_in)) return 1;
	}
	return 1;
}

static u32 get_ogg_serial_no_for_stream(char *fileName, u32 stream_num, Bool is_video)
{
    ogg_sync_state oy;
	u32 track, serial_no;
	ogg_page oggpage;
	ogg_packet oggpacket;
	ogg_stream_state os;
	FILE *f_in;

	/*means first one*/
	if (!stream_num) return 0;

	f_in = f64_open(fileName, "rb");
	if (!f_in) return 0;

	track = 0;
	serial_no = 0;
	ogg_sync_init(&oy);
	while (1) {
		if (!OGG_ReadPage(f_in, &oy, &oggpage)) break;
		if (!ogg_page_bos(&oggpage)) break;
		track ++;
		if (track != stream_num) continue;

		serial_no = ogg_page_serialno(&oggpage);
		ogg_stream_init(&os, serial_no);
		ogg_stream_pagein(&os, &oggpage);
		ogg_stream_packetpeek(&os, &oggpacket);

		if (is_video && (oggpacket.bytes >= 7) && !strncmp(&oggpacket.packet[1], "theora", 6)) {
			ogg_stream_clear(&os);		
			break;
		}
		if (!is_video && (oggpacket.bytes >= 7) && !strncmp(&oggpacket.packet[1], "vorbis", 6)) {
			ogg_stream_clear(&os);		
			break;
		}
		ogg_stream_clear(&os);
		serial_no = 0;
	}
	ogg_sync_clear(&oy);
	fclose(f_in);
	return serial_no;
}

M4Err MP4T_ImportOggVideo(M4TrackImporter *import)
{
	M4Err e;
    ogg_sync_state oy;
	u32 di, track, done, tot_size, duration;
	u32 w, h, fps_num, fps_den, keyframe_freq_force, theora_kgs, flag, dts_inc, timescale;
	Double FPS;
	Bool destroy_esd, go;
	u32 serial_no, sno, num_headers;
	ESDescriptor *esd;
	ogg_packet oggpacket;
	ogg_page oggpage;
	ogg_stream_state os;
	oggpack_buffer opb;
	BitStream *bs;
	FILE *f_in;
	M4Sample *samp;

	/*assume audio or simple AV file*/
	if (import->flags & M4TI_PROBE_ONLY) {
		f_in = f64_open(import->in_name, "rb");
		if (!f_in) return M4URLNotFound;

		import->nb_tracks = 0;
		go = 1;
	    ogg_sync_init(&oy);
		while (go) {
			if (!OGG_ReadPage(f_in, &oy, &oggpage)) break;

			if (!ogg_page_bos(&oggpage)) {
				go = 0;
				continue;
			}
			serial_no = ogg_page_serialno(&oggpage);
			ogg_stream_init(&os, serial_no);
			ogg_stream_pagein(&os, &oggpage);
			ogg_stream_packetpeek(&os, &oggpacket);

			import->tk_info[import->nb_tracks].track_num = import->nb_tracks+1;
			if ((oggpacket.bytes >= 7) && !strncmp(&oggpacket.packet[1], "theora", 6)) {
				import->tk_info[import->nb_tracks].type = M4_VisualMediaType;
				import->tk_info[import->nb_tracks].flags = M4TI_OVERRIDE_FPS;
			} else if ((oggpacket.bytes >= 7) && !strncmp(&oggpacket.packet[1], "vorbis", 6)) {
				import->tk_info[import->nb_tracks].type = M4_AudioMediaType;
				import->tk_info[import->nb_tracks].flags = 0;
			}
			ogg_stream_clear(&os);
			import->nb_tracks++;
		}
	    ogg_sync_clear(&oy);
		fclose(f_in);
		return M4OK;
	}

	if (import->flags & M4TI_USE_DATAREF) return import_message(import, M4NotSupported, "Cannot use data referencing with OGG files");


	sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 1);
	/*not our stream*/
	if (!sno && import->trackID) return M4OK;

	f_in = f64_open(import->in_name, "rb");
	if (!f_in) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	e = M4OK;
	done = 0;
	fseek(f_in, 0, SEEK_END);
	tot_size = ftell(f_in);
	fseek(f_in, 0, SEEK_SET);


	destroy_esd = 0;
	esd = import->esd;
	samp = M4_NewSample();
	
	/*avoids gcc warnings*/
	duration = 0;
	FPS = 0;
	num_headers = w = h = track = 0;

    ogg_sync_init(&oy);

	bs = NULL;
	serial_no = 0;
	go = 1;
	while (go) {
		if (!OGG_ReadPage(f_in, &oy, &oggpage)) break;

		if (ogg_page_bos(&oggpage)) {
			if (serial_no) continue;
			serial_no = ogg_page_serialno(&oggpage);
			ogg_stream_init(&os, serial_no);
			ogg_stream_pagein(&os, &oggpage);
			ogg_stream_packetpeek(&os, &oggpacket);

			/*not our stream*/
			if (sno && (sno!=serial_no)) {
				ogg_stream_clear(&os);
				serial_no = 0;
				continue;
			}
			if ((oggpacket.bytes < 7) || strncmp(&oggpacket.packet[1], "theora", 6)) {
				ogg_stream_clear(&os);
				serial_no = 0;
				continue;
			}
			/*that's ogg-theora*/
			bs = NewBitStream(oggpacket.packet, oggpacket.bytes, BS_READ);
			BS_ReadInt(bs, 80);
			w = BS_ReadU16(bs) << 4;
			h = BS_ReadU16(bs) << 4;
			BS_ReadInt(bs, 64);
			fps_num = BS_ReadU32(bs);
			fps_den = BS_ReadU32(bs);
			BS_ReadInt(bs, 80);
			BS_ReadInt(bs, 6);
			keyframe_freq_force = 1 << BS_ReadInt(bs, 5);
			theora_kgs = 0;
			keyframe_freq_force--;
			while (keyframe_freq_force) {
				theora_kgs ++;
				keyframe_freq_force >>= 1;
			}
			DeleteBitStream(bs);

		    FPS = ((Double)fps_num) / fps_den;
			/*note that we don't rewrite theora headers (just like in MPEG-4 video, systems timing overrides stream one)*/
			if (import->video_fps) FPS = import->video_fps;
			num_headers = 0;

			import_message(import, M4OK, "OGG Theora import - %.3f FPS - Resolution %d x %d", FPS, w, h);
			bs = NewBitStream(NULL, 0, BS_WRITE);
			continue;
		}
		/*FIXME - check end of stream for concatenated files?*/

		/*not our stream*/
        if (ogg_stream_pagein(&os, &oggpage) != 0) continue;
	


		while (ogg_stream_packetout(&os, &oggpacket ) > 0 ) {
			if (num_headers<3) {
				if(!w || !h) {
					e = import_message(import, M4NonCompliantBitStream, "Couldn't find Theora main header");
					goto exit;
				}
				/*copy headers*/
				BS_WriteU16(bs, oggpacket.bytes);
				BS_WriteData(bs, oggpacket.packet, oggpacket.bytes);
				num_headers++;

				/*let's go, create the track*/
				if (num_headers==3) {
					if (!esd) {
						destroy_esd = 1;
						esd = OD_NewESDescriptor(0);
					}
					get_video_timing(FPS, &timescale, &dts_inc);
					track = M4_NewTrack(import->dest, esd->ESID, M4_VisualMediaType, timescale);
					if (!track) goto exit;
					M4_SetTrackEnabled(import->dest, track, 1);
					if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
					import->final_trackID = esd->ESID;
					if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
					if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
					esd->slConfig->timestampResolution = timescale;
					if (!esd->decoderConfig->decoderSpecificInfo) esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
					BS_GetContent(bs, (unsigned char **) &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
					DeleteBitStream(bs);
					bs = NULL;
					esd->decoderConfig->streamType = M4ST_VISUAL;
					esd->decoderConfig->avgBitrate = 0;
					esd->decoderConfig->maxBitrate = 0;
					esd->decoderConfig->bufferSizeDB = 0;
					esd->decoderConfig->objectTypeIndication = 0xDF;

					e = M4_NewStreamDescription(import->dest, track, esd, NULL, NULL, &di);
					if (e) goto exit;
					M4_SetVisualEntrySize(import->dest, track, di, w, h);

					{
						Double d = import->duration;
						d *= esd->slConfig->timestampResolution;
						d /= 1000;
						duration = (u32) d;
					}
				}
				
				continue;
			}
			
			/*we don't need adedicated parser for theora, just check it's a theora frame and get its key type 
			WATCHOUT theora bitsteram is in BE*/
			oggpackB_readinit(&opb, oggpacket.packet, oggpacket.bytes);
			flag = oggpackB_read(&opb, 1);
			if (flag==0) {
				/*add packet*/
				samp->IsRAP = oggpackB_read(&opb, 1) ? 0 : 1;
				samp->data = oggpacket.packet;
				samp->dataLength = oggpacket.bytes;
				e = M4_AddSample(import->dest, track, di, samp);
				samp->DTS += dts_inc;
			}

			import_progress(import, done, tot_size);
			done += oggpacket.bytes;
			if ((duration && (samp->DTS > duration) ) || (import->flags & M4TI_DO_ABORT)) {
				go = 0;
				break;
			}
		}
	}
	import_progress(import, tot_size, tot_size);

	if (!serial_no) {
		import_message(import, M4OK, "OGG: No supported video found");
	} else {
		MP4T_RecomputeBitRate(import->dest, track);

		M4_SetMoviePLIndication(import->dest, M4_PL_VISUAL, 0xFE);
		import_progress(import, tot_size, tot_size);
	}
	
exit:
	if (bs) DeleteBitStream(bs);
	samp->data = NULL;
	M4_DeleteSample(&samp);
    ogg_sync_clear(&oy);
	if (serial_no) ogg_stream_clear(&os);
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	fclose(f_in);
	return e;
}

M4Err MP4T_ImportOggAudio(M4TrackImporter *import)
{
	M4Err e;
    ogg_sync_state oy;
	u32 di, track, done, tot_size, duration;
	s32 block_size;
	M4Sample *samp;
	Bool destroy_esd, go;
	u32 serial_no, sno, num_headers;
	ESDescriptor *esd;
	ogg_packet oggpacket;
	ogg_page oggpage;
	ogg_stream_state os;
	VorbisParser vp;
	BitStream *vbs;
	FILE *f_in;

	if (import->flags & M4TI_PROBE_ONLY) return M4OK;

	if (import->flags & M4TI_USE_DATAREF) return import_message(import, M4NotSupported, "Cannot use data referencing with OGG files");

	sno = get_ogg_serial_no_for_stream(import->in_name, import->trackID, 0);
	/*not our stream*/
	if (!sno && import->trackID) return M4OK;

	f_in = f64_open(import->in_name, "rb");
	if (!f_in) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	e = M4OK;

	done = 0;
	fseek(f_in, 0, SEEK_END);
	tot_size = ftell(f_in);
	fseek(f_in, 0, SEEK_SET);

	destroy_esd = 0;
	esd = import->esd;
	samp = M4_NewSample();
	/*avoids gcc warnings*/
	track = num_headers = duration = 0;

    ogg_sync_init(&oy);

	vbs = NULL;
	serial_no = 0;
	go = 1;
	while (go) {
		if (!OGG_ReadPage(f_in, &oy, &oggpage)) break;

		if (ogg_page_bos(&oggpage)) {
			if (serial_no) continue;
			serial_no = ogg_page_serialno(&oggpage);
			ogg_stream_init(&os, serial_no);
			ogg_stream_pagein(&os, &oggpage);
			ogg_stream_packetpeek(&os, &oggpacket);
			/*not our stream*/
			if (sno && (sno!=serial_no)) {
				ogg_stream_clear(&os);
				serial_no = 0;
				continue;
			}
			if ((oggpacket.bytes < 7) || strncmp(&oggpacket.packet[1], "vorbis", 6)) {
				ogg_stream_clear(&os);
				serial_no = 0;
				continue;
			}
			num_headers = 0;
			memset(&vp, 0, sizeof(VorbisParser));
			vbs = NewBitStream(NULL, 0, BS_WRITE);
			continue;
		}
		/*FIXME - check end of stream for concatenated files?*/

		/*not our stream*/
        if (ogg_stream_pagein(&os, &oggpage) != 0) continue;
	


		while (ogg_stream_packetout(&os, &oggpacket ) > 0 ) {
			if (num_headers<3) {
				if (!VorbisParseHeader(&vp, oggpacket.packet, oggpacket.bytes)) {
					e = import_message(import, M4NonCompliantBitStream, "Corrupted OGG Vorbis header");
					goto exit;					
				}

				/*copy headers*/
				BS_WriteU16(vbs, oggpacket.bytes);
				BS_WriteData(vbs, oggpacket.packet, oggpacket.bytes);
				num_headers++;

				/*let's go, create the track*/
				if (num_headers==3) {
					if (!vp.is_init) {
						e = import_message(import, M4NonCompliantBitStream, "Corrupted OGG Vorbis headers found");
						goto exit;					
					}

					import_message(import, M4OK, "OGG Vorbis import - sample rate %d - %d channel%s", vp.sample_rate, vp.channels, (vp.channels>1) ? "s" : "");

					if (!esd) {
						destroy_esd = 1;
						esd = OD_NewESDescriptor(0);
					}
					track = M4_NewTrack(import->dest, esd->ESID, M4_AudioMediaType, vp.sample_rate);
					if (!track) goto exit;
					M4_SetTrackEnabled(import->dest, track, 1);
					if (!esd->ESID) esd->ESID = M4_GetTrackID(import->dest, track);
					import->final_trackID = esd->ESID;
					if (!esd->decoderConfig) esd->decoderConfig = (DecoderConfigDescriptor *) OD_NewDescriptor(DecoderConfigDescriptor_Tag);
					if (!esd->slConfig) esd->slConfig = (SLConfigDescriptor *) OD_NewDescriptor(SLConfigDescriptor_Tag);
					esd->slConfig->timestampResolution = vp.sample_rate;
					if (!esd->decoderConfig->decoderSpecificInfo) esd->decoderConfig->decoderSpecificInfo = (DefaultDescriptor *) OD_NewDescriptor(DecoderSpecificInfo_Tag);
					BS_GetContent(vbs, (unsigned char **) &esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength);
					DeleteBitStream(vbs);
					vbs = NULL;
					esd->decoderConfig->streamType = M4ST_AUDIO;
					esd->decoderConfig->avgBitrate = vp.avg_r;
					esd->decoderConfig->maxBitrate = (vp.max_r>0) ? vp.max_r : vp.avg_r;
					esd->decoderConfig->bufferSizeDB = 0;
					esd->decoderConfig->objectTypeIndication = GPAC_OGG_MEDIA_OTI;

					e = M4_NewStreamDescription(import->dest, track, esd, NULL, NULL, &di);
					if (e) goto exit;
					M4_SetAudioDescriptionInfo(import->dest, track, di, vp.sample_rate, (vp.channels>1) ? 2 : 1, 16);

					{
						Double d = import->duration;
						d *= vp.sample_rate;
						d /= 1000;
						duration = (u32) d;
					}
				}
				continue;
			}
		
			block_size = VorbisCheckFrame(&vp, oggpacket.packet, oggpacket.bytes);
			if (!block_size) continue;

			/*add packet*/
			samp->IsRAP = 1;
			samp->data = oggpacket.packet;
			samp->dataLength = oggpacket.bytes;
			e = M4_AddSample(import->dest, track, di, samp);
			samp->DTS += block_size;

			import_progress(import, done, tot_size);
			done += oggpacket.bytes;
			if ((duration && (samp->DTS > duration) ) || (import->flags & M4TI_DO_ABORT)) {
				go = 0;
				break;
			}
		}
	}
	import_progress(import, tot_size, tot_size);

	if (!serial_no) {
		import_message(import, M4OK, "OGG: No supported audio found");
	} else {
		samp->data = NULL;
		M4_SetMoviePLIndication(import->dest, M4_PL_AUDIO, 0xFE);
		import_progress(import, tot_size, tot_size);

		MP4T_RecomputeBitRate(import->dest, track);
	}
	
exit:
	M4_DeleteSample(&samp);
	if (vbs) DeleteBitStream(vbs);
	if (serial_no) ogg_stream_clear(&os);
    ogg_sync_clear(&oy);
	if (esd && destroy_esd) OD_DeleteDescriptor((Descriptor **) &esd);
	fclose(f_in);
	return e;
}

/*import cubic QTVR to mp4*/
static M4Err MP4T_ImportMOV(M4TrackImporter *import)
{
	u32 i, di, w, h, tk, pano_t, nb_samp;
	Bool has_qtvr;
	M4Sample *samp;
	M4File *src;
	M4StreamContext *st;
	M4AUContext *au;
	SGCommand *com;
	M4SceneManager *ctx;
	LPSCENEGRAPH sg;
	M_Background *back;
	M_NavigationInfo *ni;
	M_Group *gr;
	ObjectDescriptorUpdate *odU;
	ObjectDescriptor *od;
	ESDescriptor *esd;

	/*nothing special to do here*/
	if (import->flags & M4TI_PROBE_ONLY) return 0;

	src = M4_MovieOpen(import->in_name, M4_OPEN_READ);
	if (!src) return import_message(import, M4URLNotFound, "Opening file %s failed", import->in_name);

	w = h = tk = 0;
	pano_t = 0;
	nb_samp = 0;

	has_qtvr = 0;
	for (i=0; i<M4_GetTrackCount(src); i++) {
		switch (M4_GetMediaType(src, i+1)) {
		case M4_VisualMediaType:
			if (M4_GetMediaSubType(src, i+1, 1) == FOUR_CHAR_INT('j', 'p', 'e', 'g')) {
				UnknownStreamDescription *udesc = M4_GetUnknownStreamDescription(src, i+1, 1);
				if ((udesc->width>w) || (udesc->height>h)) {
					w = udesc->width;
					h = udesc->height;
					tk = i+1;
					nb_samp = M4_GetSampleCount(src, i+1);
				}
				if (udesc->extension_buf) free(udesc->extension_buf);
				free(udesc);
			}
			break;
		case FOUR_CHAR_INT('q','t','v','r'):
			has_qtvr = 1;
			break;
		}
	}
	if (!has_qtvr) {
		M4_MovieDelete(src);
		return import_message(import, M4NotSupported, "QTVR not found - no conversion available for this QuickTime movie");
	}
	if (!tk) {
		M4_MovieDelete(src);
		return import_message(import, M4NonCompliantBitStream, "No associated visual track with QTVR movie");
	}
	if (nb_samp!=6) {
		M4_MovieDelete(src);
		return import_message(import, M4NotSupported, "Movie %s doesn't look a Cubic QTVR - sorry...", import->in_name);
	}

	import_message(import, M4OK, "Importing Cubic QTVR Movie");

	/*create scene*/
	sg = NewSceneGraph();
	ctx = NewSceneManager(sg);
	st = M4SM_NewStream(ctx, 1, M4ST_SCENE, 1);
	au = M4SM_NewAU(st, 0, 0, 1);
	com = SG_NewCommand(sg, SG_SceneReplace);
	ChainAddEntry(au->commands, com);
	gr = (M_Group *) SG_NewNode(sg, TAG_MPEG4_Group);
	Node_Register((SFNode *)gr, NULL);
	com->node = (SFNode *)gr;
	back = (M_Background *) SG_NewNode(sg, TAG_MPEG4_Background);
	ChainAddEntry(gr->children, back);
	Node_Register((SFNode *)back, (SFNode *)gr);

	VRML_MF_Alloc(&back->leftUrl, FT_MFURL, 1);
	back->leftUrl.vals[0].OD_ID = 2;
	VRML_MF_Alloc(&back->frontUrl, FT_MFURL, 1);
	back->frontUrl.vals[0].OD_ID = 3;
	VRML_MF_Alloc(&back->rightUrl, FT_MFURL, 1);
	back->rightUrl.vals[0].OD_ID = 4;
	VRML_MF_Alloc(&back->backUrl, FT_MFURL, 1);
	back->backUrl.vals[0].OD_ID = 5;
	VRML_MF_Alloc(&back->topUrl, FT_MFURL, 1);
	back->topUrl.vals[0].OD_ID = 6;
	VRML_MF_Alloc(&back->bottomUrl, FT_MFURL, 1);
	back->bottomUrl.vals[0].OD_ID = 7;

	ni = (M_NavigationInfo *) SG_NewNode(sg, TAG_MPEG4_NavigationInfo);
	ChainAddEntry(gr->children, ni);
	Node_Register((SFNode *)ni, (SFNode *)gr);
	VRML_MF_Reset(&ni->type, FT_MFString);
	VRML_MF_Alloc(&ni->type, FT_MFString, 1);
	ni->type.vals[0] = strdup("QTVR");

	/*create ODs*/
	st = M4SM_NewStream(ctx, 2, M4ST_OD, 1);
	au = M4SM_NewAU(st, 0, 0, 1);
	odU = (ObjectDescriptorUpdate*) OD_NewCommand(ODUpdate_Tag);
	ChainAddEntry(au->commands, odU);
	for (i=0; i<6; i++) {
		u32 odi, n_tk;
		od = (ObjectDescriptor *) OD_NewDescriptor(ObjectDescriptor_Tag);
		od->objectDescriptorID = 2+i;
		esd = OD_NewESDescriptor(2);
		esd->decoderConfig->streamType = M4ST_VISUAL;
		esd->decoderConfig->objectTypeIndication = 0x6C;
		esd->ESID = 3+i;
		ChainAddEntry(od->ESDescriptors, esd);
		ChainAddEntry(odU->objectDescriptors, od);

		/*insert image*/		
		n_tk = M4_NewTrack(import->dest, esd->ESID, M4_VisualMediaType, 1000);
		M4_NewStreamDescription(import->dest, n_tk, esd, NULL, NULL, &odi);
		M4_SetVisualEntrySize(import->dest, n_tk, odi, w, h);
		samp = M4_GetSample(src, tk, i+1, &di);
		samp->CTS_Offset = samp->DTS = 0;
		samp->IsRAP = 1;
		M4_AddSample(import->dest, n_tk, odi, samp);
		M4_DeleteSample(&samp);
	}

	M4SM_EncodeFile(ctx, import->dest, NULL, NULL, 0, 0);
	M4SM_Delete(ctx);
	SG_Delete(sg);
	M4_MovieDelete(src);
	return M4OK;
}


M4Err MP4T_ImportMedia(M4TrackImporter *importer)
{
	M4Err MP4T_ImportTimedText(M4TrackImporter *import);
	M4Err e;
	char *ext;
	if (!importer || (!importer->dest && (importer->flags!=M4TI_PROBE_ONLY)) || (!importer->in_name && !importer->orig) ) return M4BadParam;

	if (importer->orig) return MP4T_ImportMP4(importer);

	ext = strrchr(importer->in_name, '.');

	if (!strnicmp(ext, ".avi", 4)) {
		e = MP4T_ImportAVIVideo(importer);
		if (e) return e;
		return MP4T_ImportAVIAudio(importer);
	}
	else if (!strnicmp(ext, ".ogg", 4)) {
		e = MP4T_ImportOggVideo(importer);
		if (e) return e;
		return MP4T_ImportOggAudio(importer);
	}
	else if (!strnicmp(ext, ".mpg", 4) || !strnicmp(ext, ".mpeg", 5) 
		|| !strnicmp(ext, ".vob", 4) || !strnicmp(ext, ".vcd", 4) || !strnicmp(ext, ".svcd", 5)
		) {
		e = MP4T_ImportMPEG12Video(importer);
		if (e) return e;
		return MP4T_ImportMPEG12Audio(importer);
	}

	else if (!strnicmp(ext, ".mp3", 4)) 
		return MP4T_ImportMP3(importer);
	else if (!strnicmp(ext, ".media", 5) || !strnicmp(ext, ".info", 5) || !strnicmp(ext, ".nhnt", 5)) {
		return MP4T_ImportNHNT(importer);
	}
	else if (!strnicmp(ext, ".jpg", 4) || !strnicmp(ext, ".jpeg", 5)) 
		return MP4T_ImportStillImage(importer, 0x6C);
	else if (!strnicmp(ext, ".png", 4)) 
		return MP4T_ImportStillImage(importer, 0x6D);
	else if (!strnicmp(ext, ".aac", 4)) 
		return MP4T_ImportAAC_ADTS(importer);
	else if (!strnicmp(ext, ".mov", 4) ) 
		return MP4T_ImportMOV(importer);
	else if (!strnicmp(ext, ".amr", 4) || !strnicmp(ext, ".awb", 4) || !strnicmp(ext, ".smv", 4) || !strnicmp(ext, ".evc", 4)) 
		return MP4T_ImportAMR_EVRC_SMV(importer);
	else if (!strnicmp(ext, ".qcp", 4)) 
		return MP4T_ImportQCP(importer);
	else if (!strnicmp(ext, ".cmp", 4) || !strnicmp(ext, ".m4v", 4)) 
		return MP4T_ImportCMP(importer);
	else if (!strnicmp(ext, ".263", 4) || !strnicmp(ext, ".h263", 5)) 
		return MP4T_ImportH263(importer);
	else if (!strnicmp(ext, ".h264", 5) || !strnicmp(ext, ".264", 4)
		|| !strnicmp(ext, ".h26L", 5) || !strnicmp(ext, ".26l", 4)) 
		return MP4T_ImportH264(importer);

	else if (!strnicmp(ext, ".srt", 4) || !strnicmp(ext, ".sub", 4) || !strnicmp(ext, ".ttxt", 5) ) 
		return MP4T_ImportTimedText(importer);

	/*try with MP4*/
	if (M4_ProbeIsoMediaFile(importer->in_name)) {
		M4Err res;
		importer->orig = M4_MovieOpen(importer->in_name, M4_OPEN_READ);
		if (!importer->orig) return M4_GetLastError(NULL);
		res = MP4T_ImportMP4(importer);
		M4_MovieDelete(importer->orig);
		importer->orig = NULL;
		return res;
	}
	/*XML things*/
	else if (!strnicmp(ext, ".xml", 4) ) {
		e = MP4T_ImportTimedText(importer);
		if (!e) return e;
	}
		
	return import_message(importer, M4NotSupported, "Unknown input file type");
}


#endif



