in src/media_tools/dash_segmenter.c [3634:4088]
static GF_Err dasher_mp2t_segment_file(GF_DashSegInput *dash_input, const char *szOutName, GF_DASHSegmenter *dash_cfg, Bool first_in_set)
{
GF_TSSegmenter ts_seg;
Bool rewrite_input = GF_FALSE;
u8 is_pes[GF_M2TS_MAX_STREAMS];
char szOpt[100];
char SegName[GF_MAX_PATH], IdxName[GF_MAX_PATH];
char szSectionName[100], szRepURLsSecName[100];
char szCodecs[100];
const char *opt;
u32 i;
GF_Err e;
u64 start, pcr_shift, next_pcr_shift, presentationTimeOffset;
Double cumulated_duration = 0;
u32 bandwidth = 0;
u32 segment_index;
/*compute name for indexed segments*/
const char *basename = gf_dasher_strip_output_dir(dash_cfg->mpd_name, szOutName);
/*perform indexation of the file, this info will be destroyed at the end of the segment file routine*/
e = dasher_get_ts_demux(&ts_seg, dash_input->file_name, 0);
if (e) return e;
ts_seg.segment_duration = dash_cfg->segment_duration;
ts_seg.segment_at_rap = dash_cfg->segments_start_with_rap;
ts_seg.bandwidth = (u32) (ts_seg.file_size * 8 / dash_input->duration);
/*create bitstreams*/
segment_index = 1;
ts_seg.index_file = NULL;
ts_seg.index_bs = NULL;
presentationTimeOffset=0;
if (!dash_cfg->dash_ctx && (dash_cfg->use_url_template != 2)) {
#ifndef GPAC_DISABLE_ISOM_FRAGMENTS
GF_SegmentTypeBox *styp;
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, GF_TRUE, IdxName, szOutName, dash_input->representationID, dash_input->baseURL[0], dash_cfg->seg_rad_name, "six", 0, 0, 0, dash_cfg->use_segment_timeline);
ts_seg.index_file = gf_fopen(IdxName, "wb");
if (!ts_seg.index_file) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create index file %s\n", IdxName));
e = GF_IO_ERR;
goto exit;
}
ts_seg.index_bs = gf_bs_from_file(ts_seg.index_file, GF_BITSTREAM_WRITE);
styp = (GF_SegmentTypeBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_STYP);
styp->majorBrand = GF_4CC('r','i','s','x');
styp->minorVersion = 0;
styp->altBrand = (u32*)gf_malloc(sizeof(u32));
styp->altBrand[0] = styp->majorBrand;
styp->altCount = 1;
gf_isom_box_size((GF_Box *)styp);
gf_isom_box_write((GF_Box *)styp, ts_seg.index_bs);
gf_isom_box_del((GF_Box *)styp);
#endif
}
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, GF_TRUE, IdxName, basename, dash_input->representationID, dash_input->baseURL[0], dash_cfg->seg_rad_name, "six", 0, 0, 0, dash_cfg->use_segment_timeline);
ts_seg.PCR_DTS_initial_diff = (u64) -1;
ts_seg.subduration = (u32) (dash_cfg->subduration * 90000);
szSectionName[0] = 0;
if (dash_cfg->dash_ctx) {
sprintf(szSectionName, "Representation_%s", dash_input->representationID);
sprintf(szRepURLsSecName, "URLs_%s", dash_input->representationID);
/*restart where we left last time*/
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "ByteOffset");
if (opt) {
u64 offset;
sscanf(opt, LLU, &offset);
while (!feof(ts_seg.src) && !ts_seg.has_seen_pat) {
char data[NB_TSPCK_IO_BYTES];
u32 size = (u32) fread(data, 1, NB_TSPCK_IO_BYTES, ts_seg.src);
gf_m2ts_process_data(ts_seg.ts, data, size);
if (size<NB_TSPCK_IO_BYTES) break;
}
gf_m2ts_reset_parsers(ts_seg.ts);
ts_seg.base_PTS = 0;
gf_fseek(ts_seg.src, offset, SEEK_SET);
ts_seg.base_offset = offset;
ts_seg.ts->pck_number = (u32) (offset/188);
}
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "InitialDTSOffset");
if (opt) sscanf(opt, LLU, &ts_seg.PCR_DTS_initial_diff);
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "PresentationTimeOffset");
if (opt) {
sscanf(opt, LLU, &presentationTimeOffset);
presentationTimeOffset++;
}
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "DurationAtLastPass");
if (opt) sscanf(opt, LLD, &ts_seg.duration_at_last_pass);
}
/*index the file*/
while (!feof(ts_seg.src) && !ts_seg.suspend_indexing) {
char data[NB_TSPCK_IO_BYTES];
u32 size = (u32) fread(data, 1, NB_TSPCK_IO_BYTES, ts_seg.src);
gf_m2ts_process_data(ts_seg.ts, data, size);
if (size<NB_TSPCK_IO_BYTES) break;
}
if (feof(ts_seg.src)) ts_seg.suspend_indexing = 0;
if (!presentationTimeOffset) {
presentationTimeOffset = 1 + ts_seg.first_PTS;
if (dash_cfg->dash_ctx) {
sprintf(szOpt, LLU, ts_seg.first_PTS);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "PresentationTimeOffset", szOpt);
}
}
next_pcr_shift = 0;
if (!ts_seg.suspend_indexing) {
next_pcr_shift = ts_seg.last_DTS + ts_seg.last_frame_duration - ts_seg.PCR_DTS_initial_diff;
}
/* flush SIDX entry for the last packets */
m2ts_sidx_flush_entry(&ts_seg);
m2ts_sidx_finalize_size(&ts_seg, ts_seg.file_size);
GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[DASH]: Indexing done (1 sidx, %d entries).\n", ts_seg.sidx->nb_refs));
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, GF_TRUE, IdxName, basename, dash_input->representationID, dash_input->baseURL[0], gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "six", 0, 0, 0, dash_cfg->use_segment_timeline);
memset(is_pes, 0, sizeof(u8)*GF_M2TS_MAX_STREAMS);
for (i=0; i<dash_input->nb_components; i++) {
is_pes[ dash_input->components[i].ID ] = 1;
}
pcr_shift = 0;
bandwidth = dash_input->bandwidth;
if (!bandwidth) bandwidth = ts_seg.bandwidth;
if (dash_cfg->dash_ctx) {
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "Setup");
if (!opt || strcmp(opt, "yes")) {
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "Setup", "yes");
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "ID", dash_input->representationID);
} else {
if (!bandwidth) {
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "Bandwidth");
if (opt) sscanf(opt, "%u", &bandwidth);
}
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "StartIndex");
if (opt) sscanf(opt, "%u", &segment_index);
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "PCR90kOffset");
if (opt) sscanf(opt, LLU, &pcr_shift);
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szSectionName, "CumulatedDuration");
if (opt) {
u64 val;
sscanf(opt, LLU, &val);
cumulated_duration = ((Double) val) / dash_cfg->dash_scale;
}
}
}
/*write segment template for all representations*/
if (first_in_set && dash_cfg->seg_rad_name && dash_cfg->use_url_template && !dash_cfg->variable_seg_rad_name) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE, GF_TRUE, SegName, basename, dash_input->representationID, NULL, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " <SegmentTemplate timescale=\"90000\" duration=\"%d\" startNumber=\"%d\" media=\"%s\"", (u32) (90000*dash_cfg->segment_duration), segment_index, SegName);
//the SIDX we have is not compatible with the spec - until fixed, disable this
#if 0
if (!dash_cfg->dash_ctx) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE, 1, IdxName, basename, dash_input->representationID, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "six", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " index=\"%s\"", IdxName);
}
#endif
if (dash_cfg->ast_offset_ms<0) {
fprintf(dash_cfg->mpd, " availabilityTimeOffset=\"%g\"", - (Double) dash_cfg->ast_offset_ms / 1000.0);
}
fprintf(dash_cfg->mpd, "/>\n");
}
fprintf(dash_cfg->mpd, " <Representation id=\"%s\" mimeType=\"video/mp2t\"", dash_input->representationID);
szCodecs[0] = 0;
for (i=0; i<dash_input->nb_components; i++) {
if (strlen(dash_input->components[i].szCodec))
if (strlen(szCodecs))
strcat(szCodecs, ",");
strcat(szCodecs, dash_input->components[i].szCodec);
if (dash_input->components[i].width && dash_input->components[i].height)
fprintf(dash_cfg->mpd, " width=\"%u\" height=\"%u\"", dash_input->components[i].width, dash_input->components[i].height);
if (dash_input->components[i].sample_rate)
fprintf(dash_cfg->mpd, " audioSamplingRate=\"%d\"", dash_input->components[i].sample_rate);
}
if (strlen(szCodecs))
fprintf(dash_cfg->mpd, " codecs=\"%s\"", szCodecs);
fprintf(dash_cfg->mpd, " startWithSAP=\"%d\"", dash_cfg->segments_start_with_rap ? 1 : 0);
fprintf(dash_cfg->mpd, " bandwidth=\"%d\"", bandwidth);
fprintf(dash_cfg->mpd, ">\n");
/* writing Representation level descriptors */
if (dash_input->nb_rep_descs) {
for (i=0; i<dash_input->nb_rep_descs; i++) {
fprintf(dash_cfg->mpd, " %s\n", dash_input->rep_descs[i]);
}
}
for (i=0; i<ts_seg.sidx->nb_refs; i++) {
GF_SIDXReference *ref = &ts_seg.sidx->refs[i];
if (dash_cfg->max_segment_duration * ts_seg.sidx->timescale < ref->subsegment_duration) {
dash_cfg->max_segment_duration = (Double) ref->subsegment_duration;
dash_cfg->max_segment_duration /= ts_seg.sidx->timescale;
}
}
if (dash_cfg->single_file_mode==1) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, GF_TRUE, SegName, basename, dash_input->representationID, NULL, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " <BaseURL>%s</BaseURL>\n", SegName);
fprintf(dash_cfg->mpd, " <SegmentBase>\n");
fprintf(dash_cfg->mpd, " <RepresentationIndex sourceURL=\"%s\"/>\n", IdxName);
fprintf(dash_cfg->mpd, " </SegmentBase>\n");
/*we rewrite the file*/
rewrite_input = GF_TRUE;
} else {
if (dash_cfg->seg_rad_name && dash_cfg->use_url_template) {
if (dash_cfg->variable_seg_rad_name) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_TEMPLATE, GF_TRUE, SegName, basename, dash_input->representationID, NULL, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " <SegmentTemplate timescale=\"90000\" duration=\"%d\" startNumber=\"%d\" media=\"%s\"", (u32) (90000*dash_cfg->segment_duration), segment_index, SegName);
//the SIDX we have is not compatible with the spec - until fixed, disable this
#if 0
if (!dash_cfg->dash_ctx) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_INITIALIZATION_TEMPLATE, 1, IdxName, basename, dash_input->representationID, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "six", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " index=\"%s\"", IdxName);
}
#endif
if (presentationTimeOffset > 1)
fprintf(dash_cfg->mpd, " presentationTimeOffset=\""LLD"\"", presentationTimeOffset - 1);
if (dash_cfg->ast_offset_ms<0) {
fprintf(dash_cfg->mpd, " availabilityTimeOffset=\"%g\"", - (Double) dash_cfg->ast_offset_ms / 1000.0);
}
fprintf(dash_cfg->mpd, "/>\n");
} else if (presentationTimeOffset > 1) {
fprintf(dash_cfg->mpd, " <SegmentTemplate presentationTimeOffset=\""LLD"\"/>\n", presentationTimeOffset - 1);
GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[DASH]: PTSOffset "LLD" - startNumber %d - time %g\n", presentationTimeOffset - 1, segment_index, (Double) (s64) (ts_seg.sidx->earliest_presentation_time + pcr_shift) / 90000.0));
}
} else {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, GF_TRUE, SegName, basename, dash_input->representationID, NULL, gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
if (dash_cfg->single_file_mode)
fprintf(dash_cfg->mpd, " <BaseURL>%s</BaseURL>\n",SegName);
fprintf(dash_cfg->mpd, " <SegmentList timescale=\"90000\" duration=\"%d\"", (u32) (90000*dash_cfg->segment_duration));
if (presentationTimeOffset > 1)
fprintf(dash_cfg->mpd, " presentationTimeOffset=\""LLD"\"", presentationTimeOffset - 1);
if (dash_cfg->ast_offset_ms<0) {
fprintf(dash_cfg->mpd, " availabilityTimeOffset=\"%g\"", - (Double) dash_cfg->ast_offset_ms / 1000.0);
}
fprintf(dash_cfg->mpd, ">\n");
if (!dash_cfg->dash_ctx) {
fprintf(dash_cfg->mpd, " <RepresentationIndex sourceURL=\"%s\"/>\n", IdxName);
}
}
/*rewrite previous SegmentList entries*/
if ( dash_cfg->dash_ctx && ((dash_cfg->single_file_mode==2) || (!dash_cfg->single_file_mode && !dash_cfg->use_url_template))) {
/*rewrite previous URLs*/
const char *opt;
u32 count, i;
count = gf_cfg_get_key_count(dash_cfg->dash_ctx, szRepURLsSecName);
for (i=0; i<count; i++) {
const char *key_name = gf_cfg_get_key_name(dash_cfg->dash_ctx, szRepURLsSecName, i);
opt = gf_cfg_get_key(dash_cfg->dash_ctx, szRepURLsSecName, key_name);
fprintf(dash_cfg->mpd, " %s\n", opt);
}
}
if (dash_cfg->single_file_mode==2) {
start = ts_seg.sidx->first_offset;
for (i=0; i<ts_seg.sidx->nb_refs; i++) {
GF_SIDXReference *ref = &ts_seg.sidx->refs[i];
fprintf(dash_cfg->mpd, " <SegmentURL mediaRange=\""LLD"-"LLD"\"/>\n", start, start+ref->reference_size-1);
start += ref->reference_size;
}
rewrite_input = GF_TRUE;
} else {
FILE *src, *dst;
u64 pos, end;
Double current_time = cumulated_duration, dur;
src = gf_fopen(dash_input->file_name, "rb");
start = ts_seg.sidx->first_offset;
for (i=0; i<ts_seg.sidx->nb_refs; i++) {
char buf[NB_TSPCK_IO_BYTES];
GF_SIDXReference *ref = &ts_seg.sidx->refs[i];
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, GF_TRUE, SegName, szOutName, dash_input->representationID, dash_input->baseURL[0], dash_cfg->seg_rad_name, "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
/*warning - we may introduce repeated sequence number when concatenating files. We should use switching
segments to force reset of the continuity counter for all our pids - we don't because most players don't car ...*/
if (dash_cfg->use_url_template != 2) {
dst = gf_fopen(SegName, "wb");
if (!dst) {
gf_fclose(src);
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create segment file %s\n", SegName));
e = GF_IO_ERR;
goto exit;
}
dur = ref->subsegment_duration;
dur /= 90000;
gf_dasher_store_segment_info(dash_cfg, dash_input->representationID, SegName, (u64) (current_time*dash_cfg->dash_scale), (u64) (dur*dash_cfg->dash_scale));
current_time += dur;
gf_fseek(src, start, SEEK_SET);
pos = start;
end = start+ref->reference_size;
while (pos<end) {
u32 res;
u32 to_read = NB_TSPCK_IO_BYTES;
if (pos+NB_TSPCK_IO_BYTES >= end) {
to_read = (u32) (end-pos);
}
res = (u32) fread(buf, 1, to_read, src);
if (res==to_read) {
gf_m2ts_restamp(buf, res, pcr_shift, is_pes);
res = (u32) fwrite(buf, 1, to_read, dst);
}
if (res!=to_read) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: IO error while Extracting segment %03d / %03d\r", i+1, ts_seg.sidx->nb_refs));
break;
}
pos += res;
}
gf_fclose(dst);
}
start += ref->reference_size;
if (!dash_cfg->use_url_template) {
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, GF_TRUE, SegName, basename, dash_input->representationID, NULL, dash_cfg->seg_rad_name, "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
fprintf(dash_cfg->mpd, " <SegmentURL media=\"%s\"/>\n", SegName);
if (dash_cfg->dash_ctx) {
char szKey[100], szVal[4046];
sprintf(szKey, "UrlInfo%d", segment_index);
sprintf(szVal, "<SegmentURL media=\"%s\"/>", SegName);
gf_cfg_set_key(dash_cfg->dash_ctx, szRepURLsSecName, szKey, szVal);
}
}
segment_index++;
gf_set_progress("Extracting segment ", i+1, ts_seg.sidx->nb_refs);
}
gf_fclose(src);
cumulated_duration = current_time;
}
if (!dash_cfg->seg_rad_name || !dash_cfg->use_url_template) {
fprintf(dash_cfg->mpd, " </SegmentList>\n");
}
}
if (rewrite_input) {
FILE *in, *out;
u64 fsize, done;
char buf[NB_TSPCK_IO_BYTES];
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_SEGMENT, GF_TRUE, SegName, dash_cfg->seg_rad_name ? basename : szOutName, dash_input->representationID, dash_input->baseURL[0], gf_dasher_strip_output_dir(dash_cfg->mpd_name, dash_cfg->seg_rad_name), "ts", 0, bandwidth, segment_index, dash_cfg->use_segment_timeline);
out = gf_fopen(SegName, "wb");
if (!out) {
GF_LOG(GF_LOG_ERROR, GF_LOG_DASH, ("[DASH]: Cannot create segment file %s\n", SegName));
e = GF_IO_ERR;
goto exit;
}
in = gf_fopen(dash_input->file_name, "rb");
gf_fseek(in, 0, SEEK_END);
fsize = gf_ftell(in);
gf_fseek(in, 0, SEEK_SET);
done = 0;
while (1) {
u32 read = (u32) fread(buf, 1, NB_TSPCK_IO_BYTES, in);
gf_m2ts_restamp(buf, read, pcr_shift, is_pes);
fwrite(buf, 1, read, out);
done+=read;
gf_set_progress("Extracting segment ", done/188, fsize/188);
if (read<NB_TSPCK_IO_BYTES) break;
}
gf_fclose(in);
gf_fclose(out);
}
fprintf(dash_cfg->mpd, " </Representation>\n");
if (dash_cfg->dash_ctx) {
sprintf(szOpt, "%u", bandwidth);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "Bandwidth", szOpt);
sprintf(szOpt, "%u", segment_index);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "StartIndex", szOpt);
sprintf(szOpt, LLU, (u64) (dash_cfg->dash_scale*ts_seg.segment_duration) );
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "CumulatedDuration", szOpt);
sprintf(szOpt, LLU, next_pcr_shift + pcr_shift);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "PCR90kOffset", szOpt);
sprintf(szOpt, LLU, ts_seg.suspend_indexing);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "ByteOffset", ts_seg.suspend_indexing ? szOpt : NULL);
sprintf(szOpt, LLD, ts_seg.duration_at_last_pass);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "DurationAtLastPass", ts_seg.suspend_indexing ? szOpt : NULL);
sprintf(szOpt, LLU, ts_seg.PCR_DTS_initial_diff);
gf_cfg_set_key(dash_cfg->dash_ctx, szSectionName, "InitialDTSOffset", ts_seg.suspend_indexing ? szOpt : NULL);
}
if (ts_seg.sidx && ts_seg.index_bs) {
gf_isom_box_size((GF_Box *)ts_seg.sidx);
gf_isom_box_write((GF_Box *)ts_seg.sidx, ts_seg.index_bs);
}
if (ts_seg.pcrb && ts_seg.index_bs) {
gf_isom_box_size((GF_Box *)ts_seg.pcrb);
gf_isom_box_write((GF_Box *)ts_seg.pcrb, ts_seg.index_bs);
}
exit:
if (ts_seg.sidx) gf_isom_box_del((GF_Box *)ts_seg.sidx);
if (ts_seg.pcrb) gf_isom_box_del((GF_Box *)ts_seg.pcrb);
if (ts_seg.index_bs) gf_bs_del(ts_seg.index_bs);
if (ts_seg.index_file) {
gf_fclose(ts_seg.index_file);
gf_media_mpd_format_segment_name(GF_DASH_TEMPLATE_REPINDEX, GF_TRUE, IdxName, szOutName, dash_input->representationID, dash_input->baseURL[0], dash_cfg->seg_rad_name, "six", 0, 0, 0, dash_cfg->use_segment_timeline);
gf_delete_file(IdxName);
}
dasher_del_ts_demux(&ts_seg);
return e;
}