in src/media_tools/isom_tools.c [1069:1646]
GF_Err gf_media_split_svc(GF_ISOFile *file, u32 track, Bool splitAll)
{
GF_AVCConfig *svccfg, *cfg;
u32 num_svc_track, num_sample, svc_track, dst_track, ref_trackID, ref_trackNum, max_id, di, width, height, size, nalu_size_length, i, j, t, max_size, num_pps, num_sps, num_subseq, NALUnitHeader, data_offset, data_length, count, timescale, cur_extract_mode;
GF_Err e;
GF_AVCConfigSlot *slc, *sl;
AVCState avc;
s32 sps_id, pps_id;
GF_ISOSample *samp, *dst_samp;
GF_BitStream *bs, *dst_bs;
GF_BitStream ** sample_bs;
u8 nal_type, nal_hdr, track_ref_index;
char *buffer;
s32 *sps_track, *sps, *pps;
u64 offset;
Bool is_splitted;
Bool *first_sample_track, *is_subseq_pps;
u64 *first_DTS_track;
s8 sample_offset;
max_size = 4096;
e = GF_OK;
samp = dst_samp = NULL;
bs = NULL;
sample_bs = NULL;
sps_track = sps = pps = NULL;
first_DTS_track = NULL;
first_sample_track = is_subseq_pps = NULL;
buffer = NULL;
cfg = NULL;
num_svc_track=0;
cur_extract_mode = gf_isom_get_nalu_extract_mode(file, track);
gf_isom_set_nalu_extract_mode(file, track, GF_ISOM_NALU_EXTRACT_INSPECT);
svccfg = gf_isom_svc_config_get(file, track, 1);
if (!svccfg)
{
e = GF_OK;
goto exit;
}
num_sps = gf_list_count(svccfg->sequenceParameterSets);
if (!num_sps)
{
e = GF_OK;
goto exit;
}
num_pps = gf_list_count(svccfg->pictureParameterSets);
if ((gf_isom_get_avc_svc_type(file, track, 1) == GF_ISOM_AVCTYPE_SVC_ONLY) && !gf_isom_has_svc_explicit(file, track))
is_splitted = 1;
else
is_splitted = 0;
num_subseq = gf_isom_has_svc_explicit(file, track) ? num_sps - 1 : num_sps;
if (is_splitted)
{
/*this track has only one SVC ...*/
if (num_sps == 1)
{
/*use 'all' mode -> stop*/
if (splitAll)
goto exit;
/*use 'base' mode -> merge SVC tracks*/
else
{
e = gf_media_merge_svc(file, track, 0);
goto exit;
}
}
/*this file has been in 'splitbase' mode*/
else if (!splitAll)
goto exit;
}
timescale = gf_isom_get_media_timescale(file, track);
num_svc_track = splitAll ? num_subseq : 1;
max_id = gf_isom_get_track_id_max(file);
di = 0;
memset(&avc, 0, sizeof(AVCState));
avc.sps_active_idx = -1;
nalu_size_length = 8 * svccfg->nal_unit_size;
/*read all sps, but we need only the subset sequence parameter sets*/
sps = (s32 *) gf_malloc(num_subseq * sizeof(s32));
sps_track = (s32 *) gf_malloc(num_subseq * sizeof(s32));
count = 0;
for (i = 0; i < num_sps; i++)
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, i);
nal_type = slc->data[0] & 0x1F;
sps_id = gf_media_avc_read_sps(slc->data, slc->size, &avc, 0, NULL);
if (sps_id < 0) {
e = GF_NON_COMPLIANT_BITSTREAM;
goto exit;
}
if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM)
{
sps[count] = sps_id;
sps_track[count] = i;
count++;
}
}
/*for testing*/
assert(count == num_subseq);
/*read all pps*/
pps = (s32 *) gf_malloc(num_pps * sizeof(s32));
for (j = 0; j < num_pps; j++)
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j);
pps_id = gf_media_avc_read_pps(slc->data, slc->size, &avc);
if (pps_id < 0) {
e = GF_NON_COMPLIANT_BITSTREAM;
goto exit;
}
pps[j] = pps_id;
}
if (!is_splitted)
ref_trackID = gf_isom_get_track_id(file, track);
else
{
gf_isom_get_reference(file, track, GF_ISOM_REF_BASE, 1, &ref_trackNum);
ref_trackID = gf_isom_get_track_id(file, ref_trackNum);
}
buffer = (char*)gf_malloc(sizeof(char) * max_size);
/*read first sample for determinating the order of SVC tracks*/
count = 0;
samp = gf_isom_get_sample(file, track, 1, &di);
if (!samp)
{
e = gf_isom_last_error(file);
goto exit;
}
bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
offset = 0;
is_subseq_pps = (Bool *) gf_malloc(num_pps*sizeof(Bool));
for (i = 0; i < num_pps; i++)
is_subseq_pps[i] = 0;
while (gf_bs_available(bs))
{
size = gf_bs_read_int(bs, nalu_size_length);
if (size>max_size) {
buffer = (char*)gf_realloc(buffer, sizeof(char)*size);
max_size = size;
}
nal_hdr = gf_bs_read_u8(bs);
nal_type = nal_hdr & 0x1F;
gf_media_avc_parse_nalu(bs, nal_hdr, &avc);
e = gf_bs_seek(bs, offset+nalu_size_length/8);
if (e)
goto exit;
gf_bs_read_data(bs, buffer, size);
offset += size + nalu_size_length/8;
if (nal_type == GF_AVC_NALU_SVC_SLICE)
{
for (i = 0; i < num_pps; i++)
{
if (avc.s_info.pps->id == pps[i])
{
is_subseq_pps[i] = 1;
break;
}
}
if ((count > 0) && (avc.s_info.pps->sps_id == sps[count-1]))
continue;
/*verify the order of SPS, reorder if necessary*/
if (avc.s_info.pps->sps_id != sps[count])
{
for (i = count+1; i < num_subseq; i++)
{
/*swap two SPS*/
if (avc.s_info.pps->sps_id == sps[i])
{
sps[i] = sps[count];
sps[count] = avc.s_info.pps->sps_id;
sps_track[count] = i;
break;
}
}
}
count++;
}
}
gf_bs_del(bs);
bs = NULL;
gf_isom_sample_del(&samp);
samp = NULL;
for (t = 0; t < num_svc_track; t++)
{
e = GF_OK;
svc_track = gf_isom_new_track(file, t+1+max_id, GF_ISOM_MEDIA_VISUAL, timescale);
if (!svc_track)
{
e = gf_isom_last_error(file);
goto exit;
}
gf_isom_set_track_enabled(file, svc_track, 1);
gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_BASE, ref_trackID);
cfg = gf_odf_avc_cfg_new();
cfg->complete_representation = 1; //SVC
/*this layer depends on the base layer and the lower layers*/
gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_SCAL, ref_trackID);
for (i = 0; i < t; i++)
gf_isom_set_track_reference(file, svc_track, GF_ISOM_REF_SCAL, i+1+max_id);
e = gf_isom_svc_config_new(file, svc_track, cfg, NULL, NULL, &di);
if (e)
goto exit;
if (splitAll)
{
sps_id = sps[t];
width = avc.sps[sps_id].width;
height = avc.sps[sps_id].height;
gf_isom_set_visual_info(file, svc_track, di, width, height);
cfg->configurationVersion = 1;
cfg->chroma_bit_depth = 8 + avc.sps[sps_id].chroma_bit_depth_m8;
cfg->chroma_format = avc.sps[sps_id].chroma_format;
cfg->luma_bit_depth = 8 + avc.sps[sps_id].luma_bit_depth_m8;
cfg->profile_compatibility = avc.sps[sps_id].prof_compat;
cfg->AVCLevelIndication = avc.sps[sps_id].level_idc;
cfg->AVCProfileIndication = avc.sps[sps_id].profile_idc;
cfg->nal_unit_size = svccfg->nal_unit_size;
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, sps_track[t]);
sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
sl->id = slc->id;
sl->size = slc->size;
sl->data = (char*)gf_malloc(sizeof(char)*sl->size);
memcpy(sl->data, slc->data, sizeof(char)*sl->size);
gf_list_add(cfg->sequenceParameterSets, sl);
for (j = 0; j < num_pps; j++)
{
pps_id = pps[j];
if (is_subseq_pps[j] && (avc.pps[pps_id].sps_id == sps_id))
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j);
sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
sl->id = slc->id;
sl->size = slc->size;
sl->data = (char*)gf_malloc(sizeof(char)*sl->size);
memcpy(sl->data, slc->data, sizeof(char)*sl->size);
gf_list_add(cfg->pictureParameterSets, sl);
}
}
}
else
{
for (i = 0; i < num_subseq; i++)
{
sps_id = sps[i];
width = avc.sps[sps_id].width;
height = avc.sps[sps_id].height;
gf_isom_set_visual_info(file, svc_track, di, width, height);
cfg->configurationVersion = 1;
cfg->chroma_bit_depth = 8 + avc.sps[sps_id].chroma_bit_depth_m8;
cfg->chroma_format = avc.sps[sps_id].chroma_format;
cfg->luma_bit_depth = 8 + avc.sps[sps_id].luma_bit_depth_m8;
cfg->profile_compatibility = avc.sps[sps_id].prof_compat;
cfg->AVCLevelIndication = avc.sps[sps_id].level_idc;
cfg->AVCProfileIndication = avc.sps[sps_id].profile_idc;
cfg->nal_unit_size = svccfg->nal_unit_size;
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, sps_track[i]);
sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
sl->id = slc->id;
sl->size = slc->size;
sl->data = (char*)gf_malloc(sizeof(char)*sl->size);
memcpy(sl->data, slc->data, sizeof(char)*sl->size);
gf_list_add(cfg->sequenceParameterSets, sl);
for (j = 0; j < num_pps; j++)
{
pps_id = pps[j];
if (avc.pps[pps_id].sps_id == sps_id)
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j);
sl = (GF_AVCConfigSlot*)gf_malloc(sizeof(GF_AVCConfigSlot));
sl->id = slc->id;
sl->size = slc->size;
sl->data = (char*)gf_malloc(sizeof(char)*sl->size);
memcpy(sl->data, slc->data, sizeof(char)*sl->size);
gf_list_add(cfg->pictureParameterSets, sl);
}
}
}
}
e = gf_isom_svc_config_update(file, svc_track, 1, cfg, 0);
if (e)
goto exit;
gf_odf_avc_cfg_del(cfg);
cfg = NULL;
}
num_sample = gf_isom_get_sample_count(file, track);
first_sample_track = (Bool *) gf_malloc((num_svc_track+1) * sizeof(Bool));
for (t = 0; t <= num_svc_track; t++)
first_sample_track[t] = 1;
first_DTS_track = (u64 *) gf_malloc((num_svc_track+1) * sizeof(u64));
for (t = 0; t <= num_svc_track; t++)
first_DTS_track[t] = 0;
for (i = 1; i <= num_sample; i++)
{
/*reset*/
memset(buffer, 0, max_size);
samp = gf_isom_get_sample(file, track, i, &di);
if (!samp)
{
e = GF_IO_ERR;
goto exit;
}
/* Create (num_svc_track) SVC bitstreams + 1 AVC bitstream*/
sample_bs = (GF_BitStream **) gf_malloc(sizeof(GF_BitStream *) * (num_svc_track+1));
for (j = 0; j <= num_svc_track; j++)
sample_bs[j] = (GF_BitStream *) gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
/*write extractor*/
for (t = 0; t < num_svc_track; t++)
{
//reference to base layer
gf_bs_write_int(sample_bs[t+1], 14, nalu_size_length); // extractor 's size = 14
NALUnitHeader = 0; //reset
NALUnitHeader |= 0x1F000000; // NALU type = 31
gf_bs_write_u32(sample_bs[t+1], NALUnitHeader);
track_ref_index = (u8) gf_isom_has_track_reference(file, t+1+max_id, GF_ISOM_REF_SCAL, ref_trackID);
if (!track_ref_index)
{
e = GF_CORRUPTED_DATA;
goto exit;
}
gf_bs_write_u8(sample_bs[t+1], track_ref_index);
sample_offset = 0;
gf_bs_write_u8(sample_bs[t+1], sample_offset);
data_offset = 0;
gf_bs_write_u32(sample_bs[t+1], data_offset);
data_length = 0;
gf_bs_write_u32(sample_bs[t+1], data_length);
//reference to previous layer(s)
for (j = 0; j < t; j++)
{
gf_bs_write_int(sample_bs[t+1], 14, nalu_size_length);
NALUnitHeader = 0;
NALUnitHeader |= 0x1F000000;
gf_bs_write_u32(sample_bs[t+1], NALUnitHeader);
track_ref_index = (u8) gf_isom_has_track_reference(file, t+1+max_id, GF_ISOM_REF_SCAL, j+1+max_id);
if (!track_ref_index)
{
e = GF_CORRUPTED_DATA;
goto exit;
}
gf_bs_write_u8(sample_bs[t+1], track_ref_index);
sample_offset = 0;
gf_bs_write_u8(sample_bs[t+1], sample_offset);
data_offset = (j+1) * (nalu_size_length/8 + 14); // (nalu_size_length/8) bytes of NALU length field + 14 bytes of extractor per layer
gf_bs_write_u32(sample_bs[t+1], data_offset);
data_length = 0;
gf_bs_write_u32(sample_bs[t+1], data_length);
}
}
bs = gf_bs_new(samp->data, samp->dataLength, GF_BITSTREAM_READ);
offset = 0;
while (gf_bs_available(bs))
{
size = gf_bs_read_int(bs, nalu_size_length);
if (size>max_size) {
buffer = (char*)gf_realloc(buffer, sizeof(char)*size);
max_size = size;
}
nal_hdr = gf_bs_read_u8(bs);
nal_type = nal_hdr & 0x1F;
gf_media_avc_parse_nalu(bs, nal_hdr, &avc);
e = gf_bs_seek(bs, offset+nalu_size_length/8);
if (e)
goto exit;
gf_bs_read_data(bs, buffer, size);
offset += size + nalu_size_length/8;
switch (nal_type) {
case GF_AVC_NALU_PIC_PARAM:
pps_id = gf_media_avc_read_pps(buffer, size, &avc);;
j = 0;
dst_track = 0;
while (j < num_pps)
{
if (pps_id == pps[j])
break;
j++;
}
if ((j < num_pps) && (is_subseq_pps[j]))
{
if (splitAll)
{
for (t = 0; t < num_svc_track; t++)
{
if (sps[t] == avc.pps[pps_id].sps_id)
{
dst_track = t + 1;
break;
}
}
}
else
dst_track = 1;
}
dst_bs = sample_bs[dst_track];
break;
case GF_AVC_NALU_SVC_SUBSEQ_PARAM:
sps_id = gf_media_avc_read_sps(buffer, size, &avc, 0, NULL);
dst_track = 0;
if (splitAll)
{
for (t = 0; t < num_svc_track; t++)
{
if (sps[t] == sps_id)
{
dst_track = t + 1;
break;
}
}
}
else
dst_track = 1;
dst_bs = sample_bs[dst_track];
break;
case GF_AVC_NALU_SVC_SLICE:
dst_track = 0;
if (splitAll)
{
for (t = 0; t < num_svc_track; t++)
{
if (sps[t] == (avc.s_info.pps)->sps_id)
{
dst_track = t + 1;
break;
}
}
}
else
dst_track = 1;
dst_bs = sample_bs[dst_track];
break;
default:
dst_bs = sample_bs[0];
}
gf_bs_write_int(dst_bs, size, nalu_size_length);
gf_bs_write_data(dst_bs, buffer, size);
}
for (j = 0; j <= num_svc_track; j++)
{
if (gf_bs_get_position(sample_bs[j]))
{
if (first_sample_track[j])
{
first_sample_track[j] = 0;
first_DTS_track[j] = samp->DTS;
}
dst_samp = gf_isom_sample_new();
dst_samp->CTS_Offset = samp->CTS_Offset;
dst_samp->DTS = samp->DTS - first_DTS_track[j];
dst_samp->IsRAP = samp->IsRAP;
gf_bs_get_content(sample_bs[j], &dst_samp->data, &dst_samp->dataLength);
if (j) //SVC
e = gf_isom_add_sample(file, track+j, di, dst_samp);
else
e = gf_isom_update_sample(file, track, i, dst_samp, 1);
if (e)
goto exit;
gf_isom_sample_del(&dst_samp);
dst_samp = NULL;
}
gf_bs_del(sample_bs[j]);
sample_bs[j] = NULL;
}
gf_free(sample_bs);
sample_bs = NULL;
gf_bs_del(bs);
bs = NULL;
gf_isom_sample_del(&samp);
samp = NULL;
}
/*add Editlist entry if DTS of the first sample is not zero*/
for (t = 0; t <= num_svc_track; t++)
{
if (first_DTS_track[t])
{
u32 media_ts, moov_ts, offset;
u64 dur;
media_ts = gf_isom_get_media_timescale(file, t);
moov_ts = gf_isom_get_timescale(file);
offset = (u32)(first_DTS_track[t]) * moov_ts / media_ts;
dur = gf_isom_get_media_duration(file, t) * moov_ts / media_ts;
gf_isom_set_edit_segment(file, t, 0, offset, 0, GF_ISOM_EDIT_EMPTY);
gf_isom_set_edit_segment(file, t, offset, dur, 0, GF_ISOM_EDIT_NORMAL);
}
}
/*if this is a merged file*/
if (!is_splitted)
{
/*a normal stream: delete SVC config*/
if (!gf_isom_has_svc_explicit(file, track))
{
gf_isom_svc_config_del(file, track, 1);
}
else
{
s32 shift=0;
for (i = 0; i < gf_list_count(svccfg->sequenceParameterSets); i++)
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->sequenceParameterSets, i);
sps_id = gf_media_avc_read_sps(slc->data, slc->size, &avc, 0, NULL);
if (sps_id < 0) {
e = GF_NON_COMPLIANT_BITSTREAM;
goto exit;
}
nal_type = slc->data[0] & 0x1F;
if (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM)
{
gf_list_rem(svccfg->sequenceParameterSets, i);
gf_free(slc->data);
gf_free(slc);
i--;
}
}
for (j = 0; j < gf_list_count(svccfg->pictureParameterSets); j++)
{
slc = (GF_AVCConfigSlot *)gf_list_get(svccfg->pictureParameterSets, j);
pps_id = gf_media_avc_read_pps(slc->data, slc->size, &avc);
if (pps_id < 0) {
e = GF_NON_COMPLIANT_BITSTREAM;
goto exit;
}
if (is_subseq_pps[j+shift])
{
gf_list_rem(svccfg->pictureParameterSets, j);
gf_free(slc->data);
gf_free(slc);
j--;
}
}
e = gf_isom_svc_config_update(file, track, 1, svccfg, 0);
if (e)
goto exit;
}
}
/*if this is as splitted file: delete this track*/
else
{
gf_isom_remove_track(file, track);
}
exit:
if (svccfg) gf_odf_avc_cfg_del(svccfg);
if (cfg) gf_odf_avc_cfg_del(cfg);
if (samp) gf_isom_sample_del(&samp);
if (dst_samp) gf_isom_sample_del(&dst_samp);
if (bs) gf_bs_del(bs);
if (sample_bs)
{
for (i = 0; i <= num_svc_track; i++)
gf_bs_del(sample_bs[i]);
gf_free(sample_bs);
}
if (sps_track) gf_free(sps_track);
if (sps) gf_free(sps);
if (pps) gf_free(pps);
if (first_sample_track) gf_free(first_sample_track);
if (first_DTS_track) gf_free(first_DTS_track);
if (buffer) gf_free(buffer);
if (is_subseq_pps) gf_free(is_subseq_pps);
gf_isom_set_nalu_extract_mode(file, track, cur_extract_mode);
return e;
}