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*/
}