GF_Err gf_media_export_native()

in src/media_tools/media_export.c [711:1415]


GF_Err gf_media_export_native(GF_MediaExporter *dumper)
{
#ifdef GPAC_DISABLE_AV_PARSERS
	return GF_NOT_SUPPORTED;
#else
	GF_Err e = GF_OK;
	Bool add_ext;
	GF_DecoderConfig *dcfg;
	GF_GenericSampleDescription *udesc;
	char szName[1000], szEXT[5], GUID[16];
	FILE *out;
	unsigned int *qcp_rates, rt_cnt;	/*contains constants*/
	GF_AVCConfig *avccfg, *svccfg;
	GF_HEVCConfig *hevccfg, *shvccfg;
	GF_M4ADecSpecInfo a_cfg;
	const char *stxtcfg;
	GF_BitStream *bs;
	u32 track, i, di, count, m_type, m_stype, dsi_size, qcp_type;
	Bool is_ogg, has_qcp_pad, is_vobsub;
	u32 aac_type, aac_mode;
	char *dsi;
	QCPRateTable rtable[8];
	Bool is_stdout = GF_FALSE;
	Bool is_webvtt = GF_FALSE;

	dsi_size = 0;
	dsi = NULL;
	hevccfg = NULL;
	avccfg = NULL;
	svccfg = NULL;
	shvccfg = NULL;
	stxtcfg = NULL;

	if (!(track = gf_isom_get_track_by_id(dumper->file, dumper->trackID))) {
		GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Wrong track ID %d for file %s \n", dumper->trackID, gf_isom_get_filename(dumper->file)));
		return GF_BAD_PARAM;
	}

	m_type = gf_isom_get_media_type(dumper->file, track);
	m_stype = gf_isom_get_media_subtype(dumper->file, track, 1);
	has_qcp_pad = GF_FALSE;
	if (dumper->out_name && !strcmp(dumper->out_name, "std")) {
		is_stdout = GF_TRUE;
	}

	aac_mode = aac_type = 0;
	qcp_type = 0;
	is_ogg = GF_FALSE;
	is_vobsub = GF_FALSE;
	udesc = NULL;

	/* check if the output file name needs an extension or already has one */
	if (dumper->out_name) {
		char *lastPathPart;
		lastPathPart = strrchr(dumper->out_name , GF_PATH_SEPARATOR);
		if (!lastPathPart) {
			lastPathPart = dumper->out_name;
		} else {
			lastPathPart++;
		}
		if (strrchr(lastPathPart , '.')==NULL) {
			add_ext =  GF_TRUE;
		} else {
			add_ext =  GF_FALSE;
		}
	} else {
		add_ext = GF_FALSE;
	}
	strcpy(szName, dumper->out_name ? dumper->out_name : "");

	/* Find the decoder configuration:
	   - Check first if the stream is carried according to MPEG-4 Systems,
	   i.e. using the Object Descriptor Framework, to get the Decoder Specific Info (dsi)
	   - Or get it specifically depending on the media type
	   */
	dcfg = NULL;
	if ((m_stype==GF_ISOM_SUBTYPE_MPEG4) || (m_stype==GF_ISOM_SUBTYPE_MPEG4_CRYP)) {
		dcfg = gf_isom_get_decoder_config(dumper->file, track, 1);
	}
	if (dcfg) {
		switch (dcfg->streamType) {
		case GF_STREAM_VISUAL:
			switch (dcfg->objectTypeIndication) {
			case GPAC_OTI_VIDEO_MPEG4_PART2:
				if (dcfg->decoderSpecificInfo) {
					dsi = dcfg->decoderSpecificInfo->data;
					dcfg->decoderSpecificInfo->data = NULL;
					dsi_size = dcfg->decoderSpecificInfo->dataLength;
				}
				if (add_ext)
					strcat(szName, ".cmp");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-4 Visual stream to cmp");
				break;
			case GPAC_OTI_VIDEO_AVC:
			case GPAC_OTI_VIDEO_SVC:
				avccfg = gf_isom_avc_config_get(dumper->file, track, 1);
				svccfg = gf_isom_svc_config_get(dumper->file, track, 1);
				if (add_ext)
					strcat(szName, ".h264");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264");
				break;
			case GPAC_OTI_VIDEO_HEVC:
			case GPAC_OTI_VIDEO_SHVC:
				hevccfg = gf_isom_hevc_config_get(dumper->file, track, 1);
				shvccfg = gf_isom_shvc_config_get(dumper->file, track, 1);
				if (add_ext)
					strcat(szName, ".hvc");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-H HEVC stream to hevc");
				break;
			case GPAC_OTI_VIDEO_MPEG1:
				if (add_ext)
					strcat(szName, ".m1v");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-1 Visual stream to m1v");
				break;
			case GPAC_OTI_VIDEO_MPEG2_SIMPLE:
			case GPAC_OTI_VIDEO_MPEG2_MAIN:
			case GPAC_OTI_VIDEO_MPEG2_SNR:
			case GPAC_OTI_VIDEO_MPEG2_SPATIAL:
			case GPAC_OTI_VIDEO_MPEG2_HIGH:
			case GPAC_OTI_VIDEO_MPEG2_422:
				if (add_ext)
					strcat(szName, ".m2v");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-2 Visual stream to m2v");
				break;
			case GPAC_OTI_IMAGE_JPEG:
				if (add_ext)
					strcat(szName, ".jpg");
				gf_export_message(dumper, GF_OK, "Extracting JPEG image");
				break;
			case GPAC_OTI_IMAGE_PNG:
				if (add_ext)
					strcat(szName, ".png");
				gf_export_message(dumper, GF_OK, "Extracting PNG image");
				break;
			case GPAC_OTI_MEDIA_OGG:
				if (add_ext)
					strcat(szName, ".ogg");
				gf_export_message(dumper, GF_OK, "Extracting Ogg video");
				is_ogg = GF_TRUE;
				break;
			default:
				gf_odf_desc_del((GF_Descriptor *) dcfg);
				return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown media in track ID %d - use NHNT instead", dumper->trackID);
			}
			break;
		case GF_STREAM_AUDIO:
			switch (dcfg->objectTypeIndication) {
			case GPAC_OTI_AUDIO_AAC_MPEG2_MP:
			case GPAC_OTI_AUDIO_AAC_MPEG2_LCP:
			case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP:
				dsi = dcfg->decoderSpecificInfo->data;
				dcfg->decoderSpecificInfo->data = NULL;
				dsi_size = dcfg->decoderSpecificInfo->dataLength;
				if (add_ext)
					strcat(szName, ".aac");
				aac_mode = 1;
				aac_type = dcfg->objectTypeIndication - GPAC_OTI_AUDIO_AAC_MPEG2_MP;
				gf_export_message(dumper, GF_OK, "Extracting MPEG-2 AAC");
				break;
			case GPAC_OTI_AUDIO_AAC_MPEG4:
				if (!dcfg->decoderSpecificInfo) {
					gf_export_message(dumper, GF_OK, "Could not extracting MPEG-4 AAC: descriptor not found");
					gf_odf_desc_del((GF_Descriptor *) dcfg);
					return GF_NON_COMPLIANT_BITSTREAM;
				}
				dsi = dcfg->decoderSpecificInfo->data;
				dcfg->decoderSpecificInfo->data = NULL;
				dsi_size = dcfg->decoderSpecificInfo->dataLength;
				aac_mode = 2;
				if (add_ext)
					strcat(szName, ".aac");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AAC");
				break;
			case GPAC_OTI_AUDIO_MPEG2_PART3:
			case GPAC_OTI_AUDIO_MPEG1:
				if (add_ext)
					strcat(szName, ".mp3");
				gf_export_message(dumper, GF_OK, "Extracting MPEG-1/2 Audio (MP3)");
				break;
			case GPAC_OTI_MEDIA_OGG:
				if (add_ext)
					strcat(szName, ".ogg");
				is_ogg = GF_TRUE;
				gf_export_message(dumper, GF_OK, "Extracting Ogg audio");
				break;
			case GPAC_OTI_AUDIO_13K_VOICE:
				if (add_ext)
					strcat(szName, ".qcp");
				qcp_type = 1;
				memcpy(GUID, QCP_QCELP_GUID_1, sizeof(char)*16);
				gf_export_message(dumper, GF_OK, "Extracting QCELP-13K (QCP file)");
				break;
			case GPAC_OTI_AUDIO_EVRC_VOICE:
				memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16);
				qcp_type = 3;
				if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP;
				break;
			case GPAC_OTI_AUDIO_SMV_VOICE:
				qcp_type = 2;
				memcpy(GUID, QCP_SMV_GUID, sizeof(char)*16);
				if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP;
				break;
			case 0xD1: /* OTI in the user private range already assigned to GPAC's internal GPAC_OTI_SCENE_SVG_GZ */
				if (dcfg->decoderSpecificInfo && (dcfg->decoderSpecificInfo->dataLength==8)
				        && !strnicmp(dcfg->decoderSpecificInfo->data, "pvmm", 4)) {
					qcp_type = 3;
					memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16);
					if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP;
					break;
				}
			/*fall through*/
			default:
				gf_odf_desc_del((GF_Descriptor *) dcfg);
				return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown audio in track ID %d - use NHNT", dumper->trackID);
			}
			break;
		case GF_STREAM_ND_SUBPIC:
			switch (dcfg->objectTypeIndication)
			{
			case GPAC_OTI_MEDIA_SUBPIC:
				is_vobsub = GF_TRUE;
				dsi = dcfg->decoderSpecificInfo->data;
				dcfg->decoderSpecificInfo->data = NULL;
				dsi_size = dcfg->decoderSpecificInfo->dataLength;
				if (add_ext)
					strcat(szName, ".idx");
				gf_export_message(dumper, GF_OK, "Extracting NeroDigital VobSub subpicture stream");
				break;
			default:
				gf_odf_desc_del((GF_Descriptor *) dcfg);
				return gf_export_message(dumper, GF_NOT_SUPPORTED, "Unknown subpicture stream in track ID %d - use NHNT", dumper->trackID);
			}
			break;
		default:
			gf_odf_desc_del((GF_Descriptor *) dcfg);
			return gf_export_message(dumper, GF_NOT_SUPPORTED, "Cannot extract systems track ID %d in raw format - use NHNT", dumper->trackID);
		}
		gf_odf_desc_del((GF_Descriptor *) dcfg);
	} else {
		/* Not using the MPEG-4 Descriptor Framework */
		if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR) {
			if (add_ext)
				strcat(szName, ".amr");
			gf_export_message(dumper, GF_OK, "Extracting AMR Audio");
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR_WB) {
			if (add_ext)
				strcat(szName, ".awb");
			gf_export_message(dumper, GF_OK, "Extracting AMR WideBand Audio");
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_QCELP) {
			if (add_ext)
				strcat(szName, ".qcp");
			qcp_type = 1;
			memcpy(GUID, QCP_QCELP_GUID_1, sizeof(char)*16);
			gf_export_message(dumper, GF_OK, "Extracting QCELP-13K (QCP file)");
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_EVRC) {
			qcp_type = 3;
			memcpy(GUID, QCP_EVRC_GUID, sizeof(char)*16);
			if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP;
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_SMV) {
			qcp_type = 2;
			memcpy(GUID, QCP_SMV_GUID, sizeof(char)*16);
			if (dumper->flags & GF_EXPORT_PROBE_ONLY) dumper->flags |= GF_EXPORT_USE_QCP;
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_H263) {
			gf_export_message(dumper, GF_OK, "Extracting H263 Video");
			if (add_ext)
				strcat(szName, ".263");
		} else if (m_stype==GF_4CC('x','d','v','b')) {
			gf_export_message(dumper, GF_OK, "Extracting MPEG-2 Video (xdvb)");
			if (add_ext)
				strcat(szName, ".m2v");
		} else if (m_stype==GF_ISOM_SUBTYPE_3GP_DIMS) {
			return gf_media_export_nhml(dumper, GF_TRUE);
		} else if ((m_stype==GF_ISOM_SUBTYPE_AVC_H264)
		           || (m_stype==GF_ISOM_SUBTYPE_AVC2_H264)
		           || (m_stype==GF_ISOM_SUBTYPE_AVC3_H264)
		           || (m_stype==GF_ISOM_SUBTYPE_AVC4_H264)
		           || (m_stype==GF_ISOM_SUBTYPE_SVC_H264)
		          ) {
			avccfg = gf_isom_avc_config_get(dumper->file, track, 1);
			svccfg = gf_isom_svc_config_get(dumper->file, track, 1);
			if (add_ext)
				strcat(szName, ".h264");
			gf_export_message(dumper, GF_OK, "Extracting MPEG-4 AVC-H264 stream to h264");
		} else if ((m_stype==GF_ISOM_SUBTYPE_HEV1)
		           || (m_stype==GF_ISOM_SUBTYPE_HVC1)
		           || (m_stype==GF_ISOM_SUBTYPE_HVC2)
		           || (m_stype==GF_ISOM_SUBTYPE_HEV2)
		           || (m_stype==GF_ISOM_SUBTYPE_SHC1)
		           || (m_stype==GF_ISOM_SUBTYPE_SHV1)
		          ) {
			hevccfg = gf_isom_hevc_config_get(dumper->file, track, 1);
			shvccfg = gf_isom_shvc_config_get(dumper->file, track, 1);
			if (add_ext)
				strcat(szName, ".hvc");
			gf_export_message(dumper, GF_OK, "Extracting MPEG-H HEVC stream to hevc");
		} else if (m_type==GF_ISOM_MEDIA_FLASH) {
			gf_export_message(dumper, GF_OK, "Extracting Macromedia Flash Movie");
			if (add_ext)
				strcat(szName, ".swf");
		} else if (m_stype==GF_ISOM_SUBTYPE_MP3) {
			gf_export_message(dumper, GF_OK, "Extracting MPEG-1/2 Audio");
			if (add_ext)
				strcat(szName, ".mp3");
		} else if (m_stype==GF_ISOM_SUBTYPE_AC3) {
			gf_export_message(dumper, GF_OK, "Extracting AC3 Audio");
			if (add_ext) {
				GF_AC3Config *cfg = gf_isom_ac3_config_get(dumper->file, track, 1);
				if (cfg->is_ec3)
					strcat(szName, ".ec3");
				else
					strcat(szName, ".ac3");
				gf_free(cfg);
			}
		} else if (m_stype==GF_ISOM_SUBTYPE_WVTT) {
			gf_export_message(dumper, GF_OK, "Extracting WebVTT");
			is_webvtt = GF_TRUE;
			if (add_ext)
				strcat(szName, ".vtt");
		} else if ((m_stype==GF_ISOM_SUBTYPE_STXT) || (m_stype==GF_ISOM_SUBTYPE_METT)) {
			const char *mime;
			const char *encoding;
			gf_export_message(dumper, GF_OK, "Extracting %s Stream", (m_stype==GF_ISOM_SUBTYPE_STXT) ? "Simple Text" : "Text-based Metadata");
			gf_isom_stxt_get_description(dumper->file, track, 1, &mime, &encoding, &stxtcfg);
			if (add_ext) {
				if (mime && !strcmp(mime, "image/svg+xml")) {
					gf_export_message(dumper, GF_OK, "as SVG file");
					strcat(szName, ".svg");
				} else {
					strcat(szName, ".txt");
				}
			}
		} else if (m_stype==GF_ISOM_SUBTYPE_SBTT) {
			gf_export_message(dumper, GF_OK, "Extracting Text-based Subtitle Stream");
			if (add_ext)
				strcat(szName, ".txt");
		} else if (m_stype==GF_ISOM_SUBTYPE_STPP) {
			gf_export_message(dumper, GF_OK, "Extracting XML Subtitle Stream");
			if (add_ext)
				strcat(szName, ".xml");
			return gf_export_message(dumper, GF_NOT_SUPPORTED, "XML Subtitles re-assembling is not supported yet.", dumper->trackID);
		} else if (m_stype==GF_ISOM_SUBTYPE_METX) {
			gf_export_message(dumper, GF_OK, "Extracting XML Metadata Stream");
			if (add_ext)
				strcat(szName, ".xml");
		} else {
			if (add_ext) {
				strcat(szName, ".");
				strcat(szName, gf_4cc_to_str(m_stype));
			}
			udesc = gf_isom_get_generic_sample_description(dumper->file, track, 1);
			if (udesc) {
				dsi = udesc->extension_buf;
				udesc->extension_buf = NULL;
				dsi_size = udesc->extension_buf_size;
			}
			switch (m_type) {
			case GF_ISOM_MEDIA_VISUAL:
				gf_export_message(dumper, GF_OK, "Extracting \'%s\' Video - Compressor %s", szEXT, udesc ? udesc->compressor_name : "Unknown");
				break;
			case GF_ISOM_MEDIA_AUDIO:
				gf_export_message(dumper, GF_OK, "Extracting \'%s\' Audio - Compressor %s", szEXT, udesc ? udesc->compressor_name : "Unknown");
				break;
			default:
				gf_export_message(dumper, GF_OK, "Extracting \'%s\' Track (type '%s') - Compressor %s", szEXT, gf_4cc_to_str(m_type), udesc ? udesc->compressor_name : "Unknown");
				break;
			}
			if (udesc) gf_free(udesc);
		}
	}
	if (qcp_type>1) {
		if (dumper->flags & GF_EXPORT_USE_QCP) {
			if (add_ext)
				strcat(szName, ".qcp");
			gf_export_message(dumper, GF_OK, "Extracting %s audio (QCP file)", (qcp_type==2) ? "SMV" : "EVRC");
		} else if (qcp_type==2) {
			if (add_ext)
				strcat(szName, ".smv");
			gf_export_message(dumper, GF_OK, "Extracting SMV audio");
		} else {
			if (add_ext)
				strcat(szName, ".evc");
			gf_export_message(dumper, GF_OK, "Extracting EVRC audio");
		}
	}
	if (aac_mode) {
		gf_m4a_get_config(dsi, dsi_size, &a_cfg);
		if (aac_mode==2) aac_type = a_cfg.base_object_type - 1;
		gf_free(dsi);
		dsi = NULL;
	}
	if (dumper->flags & GF_EXPORT_SVC_LAYER) {
		gf_isom_set_nalu_extract_mode(dumper->file, track, GF_ISOM_NALU_EXTRACT_LAYER_ONLY);
	}
	/* The stream can be extracted and we found its DSI or config (if any), and the file extension is set up */

	if (dumper->flags & GF_EXPORT_PROBE_ONLY) {
		/* We don't need to go further in this mode */
		if (dsi) gf_free(dsi);
		if (avccfg) gf_odf_avc_cfg_del(avccfg);
		if (svccfg) gf_odf_avc_cfg_del(svccfg);
		if (hevccfg) gf_odf_hevc_cfg_del(hevccfg);
		if (shvccfg) gf_odf_hevc_cfg_del(shvccfg);
		return GF_OK;
	}

	if (is_ogg) {
		return gf_dump_to_ogg(dumper, is_stdout ? NULL : szName, track);
	}
	if (is_vobsub) {
		return gf_dump_to_vobsub(dumper, szName, track, dsi, dsi_size);
	}
	if (is_webvtt) {
#ifndef GPAC_DISABLE_TTXT
		GF_Err gf_webvtt_dump_iso_track(GF_MediaExporter *dumper, char *szName, u32 track, Bool merge);
		return gf_webvtt_dump_iso_track(dumper, szName, track, (dumper->flags & GF_EXPORT_WEBVTT_NOMERGE? GF_FALSE : GF_TRUE));
#else
		return GF_NOT_SUPPORTED;
#endif
	}

	if (is_stdout) {
		out = stdout;
	} else if (dumper->out_name && (dumper->flags & GF_EXPORT_MERGE)) {
		out = gf_fopen(dumper->out_name, "a+b");
		if (out) gf_fseek(out, 0, SEEK_END);
	} else {
		out = gf_fopen(szName, "wb");
	}
	if (!out) {
		if (dsi) gf_free(dsi);
		if (avccfg) gf_odf_avc_cfg_del(avccfg);
		if (svccfg) gf_odf_avc_cfg_del(svccfg);
		if (hevccfg) gf_odf_hevc_cfg_del(hevccfg);
		if (shvccfg) gf_odf_hevc_cfg_del(shvccfg);
		return gf_export_message(dumper, GF_IO_ERR, "Error opening %s for writing - check disk access & permissions", szName);
	}

	/* Start writing the stream out */
	bs = gf_bs_from_file(out, GF_BITSTREAM_WRITE);

	if (dsi) {
		gf_bs_write_data(bs, dsi, dsi_size);
		gf_free(dsi);
	}

	if (avccfg) {
		DUMP_AVCPARAM(avccfg->sequenceParameterSets);
		DUMP_AVCPARAM(avccfg->pictureParameterSets);
	}

	if (hevccfg) {
		DUMP_HEVCPARAM(hevccfg);
	}

	if (svccfg || shvccfg) {
		if (!(dumper->flags & GF_EXPORT_SVC_LAYER)) {
			GF_AVCConfig *cfg;
			GF_HEVCConfig *hcfg;
			u32 ref_track = 0, t;
			s32 countRef;

			// copy avcC and svcC from base layer
			gf_isom_get_reference(dumper->file, track, GF_ISOM_REF_BASE, 1, &ref_track);
			cfg = gf_isom_avc_config_get(dumper->file, ref_track, 1);
			hcfg = gf_isom_hevc_config_get(dumper->file, ref_track, 1);
			if (cfg) {
				DUMP_AVCPARAM(cfg->sequenceParameterSets);
				DUMP_AVCPARAM(cfg->pictureParameterSets);
				gf_odf_avc_cfg_del(cfg);
				cfg = NULL;
			}
			if (hcfg) {
				DUMP_HEVCPARAM(hcfg);
				DUMP_HEVCPARAM(hcfg);
				gf_odf_hevc_cfg_del(hcfg);
				hcfg = NULL;
			}

			// copy avcC and svcC from lower layer
			countRef = gf_isom_get_reference_count(dumper->file, track, GF_ISOM_REF_SCAL);
			if (countRef < 0) {
				e = gf_isom_last_error(dumper->file);
				goto exit;
			}
			for (t = 2; t <= (u32) countRef; t++) { // referenceIndex 1 is the base layer
				gf_isom_get_reference(dumper->file, track, GF_ISOM_REF_SCAL, t, &ref_track);
				cfg = gf_isom_svc_config_get(dumper->file, ref_track, 1);
				if (cfg) {
					DUMP_AVCPARAM(cfg->sequenceParameterSets);
					DUMP_AVCPARAM(cfg->pictureParameterSets);
					gf_odf_avc_cfg_del(cfg);
					cfg = NULL;
				}
				hcfg = gf_isom_shvc_config_get(dumper->file, ref_track, 1);
				if (hcfg) {
					DUMP_HEVCPARAM(hcfg);
					gf_odf_hevc_cfg_del(hcfg);
					hcfg = NULL;
				}
			}
		}
		if (svccfg) {
			DUMP_AVCPARAM(svccfg->sequenceParameterSets);
			DUMP_AVCPARAM(svccfg->pictureParameterSets);
		}
		if (shvccfg) {
			DUMP_HEVCPARAM(shvccfg);
		}
	}


	if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR) {
		gf_bs_write_data(bs, "#!AMR\n", 6);
	} else if (m_stype==GF_ISOM_SUBTYPE_3GP_AMR_WB) {
		gf_bs_write_data(bs, "#!AMR-WB\n", 9);
	} else if ((qcp_type>1) && !(dumper->flags & GF_EXPORT_USE_QCP)) {
		if (qcp_type==2) gf_bs_write_data(bs, "#!SMV\n", 6);
		else gf_bs_write_data(bs, "#!EVRC\n", 7);
		qcp_type = 0;
	} else if ((m_stype==GF_ISOM_SUBTYPE_STXT) || (m_stype==GF_ISOM_SUBTYPE_METT)) {
		if (stxtcfg) {
			gf_bs_write_data(bs, stxtcfg, (u32)strlen(stxtcfg));
		}
	}

	count = gf_isom_get_sample_count(dumper->file, track);

	/*QCP formatting*/
	qcp_rates = NULL;
	rt_cnt = 0;
	if (qcp_type) {
		GF_ISOSample *samp;
		Bool needs_rate_octet;
		u32 tot_size, data_size, sample_size, avg_rate, agg_samp;
		u32 block_size = 160;
		u32 sample_rate = 8000;
		GF_3GPConfig *gpc = gf_isom_3gp_config_get(dumper->file, track, 1);
		sample_size = gf_isom_get_constant_sample_size(dumper->file, track);
		agg_samp = 1;
		if (gpc) {
			agg_samp = gpc->frames_per_sample;
			gf_free(gpc);
		}

		if (qcp_type==1) {
			qcp_rates = (unsigned int*)GF_QCELP_RATE_TO_SIZE;
			rt_cnt = GF_QCELP_RATE_TO_SIZE_NB;
		} else {
			qcp_rates = (unsigned int*)GF_SMV_EVRC_RATE_TO_SIZE;
			rt_cnt = GF_SMV_EVRC_RATE_TO_SIZE_NB;
		}

		/*if cst size and no aggregation, skip table*/
		if (sample_size && !agg_samp) {
			data_size = sample_size*count;
		} else {
			/*dumps full table...*/
			for (i=0; i<rt_cnt; i++) {
				rtable[i].rate_idx = qcp_rates[2*i];
				rtable[i].pck_size = qcp_rates[2*i+1];
			}
			data_size = (u32) gf_isom_get_media_data_size(dumper->file, track);
		}

		/*check sample format - packetvideo doesn't include rate octet...*/
		needs_rate_octet = GF_TRUE;
		samp = gf_isom_get_sample_info(dumper->file, track, 1, NULL, NULL);
		for (i=0; i<rt_cnt; i++) {
			if (qcp_rates[2*i+1]==samp->dataLength) {
				needs_rate_octet = GF_FALSE;
				break;
			}
		}
		gf_isom_sample_del(&samp);
		if (needs_rate_octet) data_size += count;
		has_qcp_pad = (data_size % 2) ? GF_TRUE : GF_FALSE;

		avg_rate = 8*data_size*sample_rate/count/block_size;
		/*QLCM + fmt + vrat + data*/
		tot_size = 4+ 8+150 + 8+8 + 8 + data_size;
		/*pad is included in riff size*/
		if (has_qcp_pad) tot_size++;

		gf_bs_write_data(bs, "RIFF", 4);
		gf_bs_write_u32_le(bs, tot_size);
		gf_bs_write_data(bs, "QLCM", 4);
		gf_bs_write_data(bs, "fmt ", 4);
		gf_bs_write_u32_le(bs, 150);/*fmt chunk size*/
		gf_bs_write_u8(bs, 1);
		gf_bs_write_u8(bs, 0);
		gf_bs_write_data(bs, GUID, 16);
		gf_bs_write_u16_le(bs, 1);
		memset(szName, 0, 80);
		strcpy(szName, (qcp_type==1) ? "QCELP-GPACExport" : ((qcp_type==2) ? "SMV-GPACExport" : "EVRC-GPACExport"));
		gf_bs_write_data(bs, szName, 80);
		gf_bs_write_u16_le(bs, avg_rate);
		gf_bs_write_u16_le(bs, sample_size);
		gf_bs_write_u16_le(bs, block_size);
		gf_bs_write_u16_le(bs, sample_rate);
		gf_bs_write_u16_le(bs, 16);
		gf_bs_write_u32_le(bs, rt_cnt);
		for (i=0; i<8; i++) {
			if (i<rt_cnt) {
				/*frame size MINUS rate octet*/
				gf_bs_write_u8(bs, rtable[i].pck_size - 1);
				gf_bs_write_u8(bs, rtable[i].rate_idx);
			} else {
				gf_bs_write_u16(bs, 0);
			}
		}
		memset(szName, 0, 80);
		gf_bs_write_data(bs, szName, 20);/*reserved*/
		gf_bs_write_data(bs, "vrat", 4);
		gf_bs_write_u32_le(bs, 8);/*vrat chunk size*/
		gf_bs_write_u32_le(bs, rt_cnt);
		gf_bs_write_u32_le(bs, count);
		gf_bs_write_data(bs, "data", 4);
		gf_bs_write_u32_le(bs, data_size);/*data chunk size*/

		qcp_type = needs_rate_octet ? 1 : 0;
	}

	/* Start exporting samples */
	for (i=0; i<count; i++) {
		GF_ISOSample *samp = gf_isom_get_sample(dumper->file, track, i+1, &di);
		if (!samp) {
			e = gf_isom_last_error(dumper->file);
			break;
		}
		/*AVC sample to NALU*/
		if (avccfg || svccfg || hevccfg || shvccfg) {
			u32 j, nal_size, remain, nal_unit_size;
			char *ptr = samp->data;
			nal_unit_size = 0;
			if (avccfg) nal_unit_size= avccfg->nal_unit_size;
			else if (svccfg) nal_unit_size = svccfg->nal_unit_size;
			else if (hevccfg) nal_unit_size = hevccfg->nal_unit_size;
			else if (shvccfg) nal_unit_size = shvccfg->nal_unit_size;
			remain = samp->dataLength;
			while (remain) {
				nal_size = 0;
				if (remain<nal_unit_size) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Sample %d (size %d): Corrupted NAL Unit: header size %d - bytes left %d\n", i+1, samp->dataLength, nal_unit_size, remain) );
					break;
				}
				for (j=0; j<nal_unit_size; j++) {
					nal_size |= ((u8) *ptr);
					if (j+1<nal_unit_size) nal_size<<=8;
					remain--;
					ptr++;
				}
				gf_bs_write_u32(bs, 1);
				if (remain < nal_size) {
					GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("Sample %d (size %d): Corrupted NAL Unit: size %d - bytes left %d\n", i+1, samp->dataLength, nal_size, remain) );
					nal_size = remain;
				}
				gf_bs_write_data(bs, ptr, nal_size);
				ptr += nal_size;
				remain -= nal_size;
			}
		}
		/*adts frame header*/
		else if (aac_mode > 0) {
			gf_bs_write_int(bs, 0xFFF, 12);/*sync*/
			gf_bs_write_int(bs, (aac_mode==1) ? 1 : 0, 1);/*mpeg2 aac*/
			gf_bs_write_int(bs, 0, 2); /*layer*/
			gf_bs_write_int(bs, 1, 1); /* protection_absent*/
			gf_bs_write_int(bs, aac_type, 2);
			gf_bs_write_int(bs, a_cfg.base_sr_index, 4);
			gf_bs_write_int(bs, 0, 1);
			gf_bs_write_int(bs, a_cfg.nb_chan, 3);
			gf_bs_write_int(bs, 0, 4);
			gf_bs_write_int(bs, 7+samp->dataLength, 13);
			gf_bs_write_int(bs, 0x7FF, 11);
			gf_bs_write_int(bs, 0, 2);
		}
		/*fix rate octet for QCP*/
		else if (qcp_type) {
			u32 j;
			for (j=0; j<rt_cnt; j++) {
				if (qcp_rates[2*j+1]==1+samp->dataLength) {
					gf_bs_write_u8(bs, qcp_rates[2*j]);
					break;
				}
			}
		}
		if (!avccfg && !svccfg && !hevccfg && !shvccfg &!is_webvtt) {
			gf_bs_write_data(bs, samp->data, samp->dataLength);
		}
		gf_isom_sample_del(&samp);
		gf_set_progress("Media Export", i+1, count);
		if (dumper->flags & GF_EXPORT_DO_ABORT) break;
	}
	if (has_qcp_pad) gf_bs_write_u8(bs, 0);
exit:
	if (avccfg) gf_odf_avc_cfg_del(avccfg);
	if (svccfg) gf_odf_avc_cfg_del(svccfg);
	if (hevccfg) gf_odf_hevc_cfg_del(hevccfg);
	if (shvccfg) gf_odf_hevc_cfg_del(shvccfg);
	gf_bs_del(bs);
	if (!is_stdout)
		gf_fclose(out);
	return e;
#endif /*GPAC_DISABLE_AV_PARSERS*/
}