This source file includes following definitions.
- gf_m2ts_time_less
- gf_m2ts_time_equal
- gf_m2ts_time_less_or_equal
- gf_m2ts_time_inc
- gf_m2ts_time_diff_us
- gf_m2ts_mux_table_update
- gf_m2ts_mux_table_update_bitrate
- gf_m2ts_mux_table_update_mpeg4
- gf_m2ts_add_adaptation
- gf_m2ts_mux_table_get_next_packet
- gf_m2ts_stream_process_sdt
- gf_m2ts_stream_process_pat
- gf_m2ts_stream_process_pmt
- gf_m2ts_remap_timestamps_for_pes
- id3_tag_create
- gf_m2ts_adjust_next_stream_time_for_pcr
- gf_m2ts_stream_process_pes
- gf_m2ts_get_pcr
- gf_m2ts_stream_update_data_following
- gf_m2ts_stream_compute_pes_length
- gf_m2ts_stream_get_pes_header_length
- gf_m2ts_stream_add_pes_header
- gf_m2ts_mux_pes_get_next_packet
- gf_m2ts_stream_new
- gf_m2ts_output_ctrl
- gf_m2ts_stream_set_default_slconfig
- gf_m2ts_stream_index
- gf_m2ts_stream_add_hierarchy_descriptor
- gf_m2ts_stream_add_metadata_pointer_descriptor
- gf_m2ts_stream_add_metadata_descriptor
- gf_m2ts_program_stream_add
- gf_m2ts_program_stream_update_ts_scale
- gf_m2ts_mux_program_find
- gf_m2ts_mux_program_add
- gf_m2ts_mux_program_set_name
- gf_m2ts_mux_new
- gf_m2ts_mux_enable_sdt
- gf_m2ts_mux_set_pcr_max_interval
- gf_m2ts_mux_stream_del
- gf_m2ts_mux_program_del
- gf_m2ts_mux_del
- gf_m2ts_mux_update_config
- gf_m2ts_get_sys_clock
- gf_m2ts_get_ts_clock
- gf_m2ts_mux_use_single_au_pes_mode
- gf_m2ts_mux_set_initial_pcr
- gf_m2ts_mux_enable_pcr_only_packets
- gf_m2ts_mux_process
#include <gpac/mpegts.h>
#include <gpac/constants.h>
#include <gpac/media_tools.h>
#if !defined(GPAC_DISABLE_MPEG2TS_MUX)
#define BITRATE_UPDATE_WINDOW 90000
#define ADAPTATION_LENGTH_LENGTH 1
#define ADAPTATION_FLAGS_LENGTH 1
#define ADAPTATION_EXTENSION_LENGTH_LENGTH 1
#define ADAPTATION_EXTENSION_FLAGS_LENGTH 1
#define PCR_LENGTH 6
static GFINLINE Bool gf_m2ts_time_less(GF_M2TS_Time *a, GF_M2TS_Time *b) {
if (a->sec>b->sec) return GF_FALSE;
if (a->sec==b->sec) return (a->nanosec<b->nanosec) ? GF_TRUE : GF_FALSE;
return GF_TRUE;
}
static GFINLINE Bool gf_m2ts_time_equal(GF_M2TS_Time *a, GF_M2TS_Time *b) {
return ((a->sec==b->sec) && (a->nanosec == b->nanosec) );
}
static GFINLINE Bool gf_m2ts_time_less_or_equal(GF_M2TS_Time *a, GF_M2TS_Time *b) {
if (a->sec>b->sec) return GF_FALSE;
if (a->sec==b->sec) return (a->nanosec>b->nanosec) ? GF_FALSE : GF_TRUE;
return GF_TRUE;
}
static GFINLINE void gf_m2ts_time_inc(GF_M2TS_Time *time, u64 delta_inc_num, u32 delta_inc_den)
{
u64 n_sec;
u64 sec;
if (!delta_inc_den) return;
sec = delta_inc_num / delta_inc_den;
if (sec) {
time->sec += (u32) sec;
sec *= delta_inc_den;
delta_inc_num = delta_inc_num % sec;
}
n_sec = delta_inc_num;
n_sec *= 0x3B9ACA00;
n_sec /= delta_inc_den;
time->nanosec += (u32) n_sec;
while (time->nanosec >= 0x3B9ACA00) {
time->nanosec -= 0x3B9ACA00;
time->sec ++;
}
}
static GFINLINE s32 gf_m2ts_time_diff_us(GF_M2TS_Time *a, GF_M2TS_Time *b)
{
s32 drift = b->nanosec;
drift -= a->nanosec;
drift /= 1000;
if (a->sec != b->sec) {
drift += (b->sec - a->sec) * 1000000;
}
return drift;
}
void gf_m2ts_mux_table_update(GF_M2TS_Mux_Stream *stream, u8 table_id, u16 table_id_extension,
u8 *table_payload, u32 table_payload_length,
Bool use_syntax_indicator, Bool private_indicator,
Bool use_checksum)
{
u32 overhead_size;
u32 offset;
u32 section_number, nb_sections;
GF_M2TS_Mux_Table *table, *prev_table;
u32 maxSectionLength;
GF_M2TS_Mux_Section *section, *prev_sec;
GF_BitStream *bs;
prev_table = NULL;
table = stream->tables;
while (table) {
if (table->table_id == table_id) {
GF_M2TS_Mux_Section *sec = table->section;
while (sec) {
GF_M2TS_Mux_Section *sec2 = sec->next;
gf_free(sec->data);
gf_free(sec);
sec = sec2;
}
table->version_number = (table->version_number + 1)%0x1F;
break;
}
prev_table = table;
table = table->next;
}
if (!table) {
GF_SAFEALLOC(table, GF_M2TS_Mux_Table);
if (!table) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate table id %d\n", stream->pid, table_id));
return;
}
table->table_id = table_id;
if (prev_table) prev_table->next = table;
else stream->tables = table;
}
if (!table_payload_length) return;
switch (table_id) {
case GF_M2TS_TABLE_ID_PMT:
case GF_M2TS_TABLE_ID_PAT:
case GF_M2TS_TABLE_ID_SDT_ACTUAL:
case GF_M2TS_TABLE_ID_SDT_OTHER:
case GF_M2TS_TABLE_ID_TDT:
case GF_M2TS_TABLE_ID_TOT:
case GF_M2TS_TABLE_ID_BAT:
maxSectionLength = 1024;
break;
case GF_M2TS_TABLE_ID_MPEG4_BIFS:
case GF_M2TS_TABLE_ID_MPEG4_OD:
maxSectionLength = 4096;
break;
default:
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Cannot create sections for table id %d\n", stream->pid, table_id));
return;
}
overhead_size = SECTION_HEADER_LENGTH;
if (use_syntax_indicator) overhead_size += SECTION_ADDITIONAL_HEADER_LENGTH + CRC_LENGTH;
section_number = 0;
nb_sections = 1;
while (nb_sections*(maxSectionLength - overhead_size)<table_payload_length) nb_sections++;
switch (table_id) {
case GF_M2TS_TABLE_ID_PMT:
if (nb_sections > 1)
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] last section number for PMT shall be 0\n"));
break;
default:
break;
}
prev_sec = NULL;
offset = 0;
while (offset < table_payload_length) {
u32 remain;
GF_SAFEALLOC(section, GF_M2TS_Mux_Section);
if (!section) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate section for table id %d\n", stream->pid, table_id));
return;
}
remain = table_payload_length - offset;
if (remain > maxSectionLength - overhead_size) {
section->length = maxSectionLength;
} else {
section->length = remain + overhead_size;
}
bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, table_id, 8);
gf_bs_write_int(bs, use_syntax_indicator, 1);
gf_bs_write_int(bs, private_indicator, 1);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12);
if (use_syntax_indicator) {
gf_bs_write_int(bs, table_id_extension, 16);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, table->version_number, 5);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, section_number, 8);
section_number++;
gf_bs_write_int(bs, nb_sections-1, 8);
}
gf_bs_write_data(bs, (char *) table_payload + offset, section->length - overhead_size);
offset += section->length - overhead_size;
if (use_syntax_indicator) {
gf_bs_write_u32(bs, 0);
}
gf_bs_get_content(bs, (char**) §ion->data, §ion->length);
gf_bs_del(bs);
if (use_syntax_indicator) {
u32 CRC;
CRC = gf_crc_32((char *) section->data,section->length-CRC_LENGTH);
section->data[section->length-4] = (CRC >> 24) & 0xFF;
section->data[section->length-3] = (CRC >> 16) & 0xFF;
section->data[section->length-2] = (CRC >> 8) & 0xFF;
section->data[section->length-1] = CRC & 0xFF;
}
if (prev_sec) prev_sec->next = section;
else table->section = section;
prev_sec = section;
}
stream->current_table = stream->tables;
stream->current_section = stream->current_table->section;
stream->current_section_offset = 0;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Generating %d sections for table id %d - version number %d - extension ID %d\n", stream->pid, nb_sections, table_id, table->version_number, table_id_extension));
}
void gf_m2ts_mux_table_update_bitrate(GF_M2TS_Mux *mux, GF_M2TS_Mux_Stream *stream)
{
GF_M2TS_Mux_Table *table;
if (stream->table_needs_update)
stream->process(mux, stream);
stream->bit_rate = 0;
table = stream->tables;
while (table) {
GF_M2TS_Mux_Section *section = table->section;
while (section) {
u32 nb_bytes = 0;
while (nb_bytes<section->length) nb_bytes += 188;
stream->bit_rate += nb_bytes;
section = section->next;
}
table = table->next;
}
stream->bit_rate *= 8;
stream->bit_rate *= 1000;
if (stream->refresh_rate_ms) {
stream->bit_rate /= stream->refresh_rate_ms;
} else if (stream->table_needs_send) {
stream->bit_rate /= 100;
} else {
stream->bit_rate = 0;
}
}
void gf_m2ts_mux_table_update_mpeg4(GF_M2TS_Mux_Stream *stream, u8 table_id, u16 table_id_extension,
char *table_payload, u32 table_payload_length,
Bool use_syntax_indicator, Bool private_indicator,
Bool increment_version_number, Bool use_checksum)
{
GF_SLHeader hdr;
u32 overhead_size;
u32 offset, sl_size;
u32 section_number, nb_sections;
GF_M2TS_Mux_Table *table, *prev_table;
u32 maxSectionLength = 4096;
GF_M2TS_Mux_Section *section, *prev_sec;
GF_BitStream *bs;
prev_table = NULL;
table = stream->tables;
while (table) {
if (table->table_id == table_id) {
GF_M2TS_Mux_Section *sec = table->section;
while (sec) {
GF_M2TS_Mux_Section *sec2 = sec->next;
gf_free(sec->data);
gf_free(sec);
sec = sec2;
}
if (increment_version_number)
table->version_number = (table->version_number + 1)%0x1F;
break;
}
prev_table = table;
table = table->next;
}
if (!table) {
GF_SAFEALLOC(table, GF_M2TS_Mux_Table);
if (!table) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate table id %d\n", stream->pid, table_id));
return;
}
table->table_id = table_id;
if (prev_table) prev_table->next = table;
else stream->tables = table;
}
if (!table_payload_length) return;
overhead_size = SECTION_HEADER_LENGTH;
if (use_syntax_indicator) overhead_size += SECTION_ADDITIONAL_HEADER_LENGTH + CRC_LENGTH;
section_number = 0;
nb_sections = 1;
hdr = stream->sl_header;
sl_size = gf_sl_get_header_size(stream->ifce->sl_config, &hdr);
if (sl_size + table_payload_length > maxSectionLength - overhead_size) {
nb_sections = 0;
offset = 0;
hdr.accessUnitEndFlag = 0;
while (offset<table_payload_length) {
sl_size = gf_sl_get_header_size(stream->ifce->sl_config, &hdr);
hdr.accessUnitStartFlag = 0;
offset += maxSectionLength - overhead_size - sl_size;
nb_sections++;
}
}
prev_sec = NULL;
offset = 0;
hdr = stream->sl_header;
while (offset < table_payload_length) {
u32 remain;
char *slhdr;
u32 slhdr_size;
GF_SAFEALLOC(section, GF_M2TS_Mux_Section);
if (!section) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate section for table id %d\n", stream->pid, table_id));
return;
}
hdr.accessUnitEndFlag = (section_number+1==nb_sections) ? stream->sl_header.accessUnitEndFlag : 0;
gf_sl_packetize(stream->ifce->sl_config, &hdr, NULL, 0, &slhdr, &slhdr_size);
hdr.accessUnitStartFlag = 0;
remain = table_payload_length - offset;
if (remain > maxSectionLength - overhead_size - slhdr_size) {
section->length = maxSectionLength;
} else {
section->length = remain + overhead_size + slhdr_size;
}
sl_size = section->length - overhead_size - slhdr_size;
bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, table_id, 8);
gf_bs_write_int(bs, use_syntax_indicator, 1);
gf_bs_write_int(bs, private_indicator, 1);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12);
if (use_syntax_indicator) {
gf_bs_write_int(bs, table_id_extension, 16);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, table->version_number, 5);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, section_number, 8);
section_number++;
gf_bs_write_int(bs, nb_sections-1, 8);
}
gf_bs_write_data(bs, slhdr, slhdr_size);
gf_free(slhdr);
gf_bs_write_data(bs, (char *) table_payload + offset, sl_size);
offset += sl_size;
if (use_syntax_indicator) {
gf_bs_write_u32(bs, 0);
}
gf_bs_get_content(bs, (char**) §ion->data, §ion->length);
gf_bs_del(bs);
if (use_syntax_indicator) {
u32 CRC;
CRC = gf_crc_32((char *) section->data,section->length-CRC_LENGTH);
section->data[section->length-4] = (CRC >> 24) & 0xFF;
section->data[section->length-3] = (CRC >> 16) & 0xFF;
section->data[section->length-2] = (CRC >> 8) & 0xFF;
section->data[section->length-1] = CRC & 0xFF;
}
if (prev_sec) prev_sec->next = section;
else table->section = section;
prev_sec = section;
}
stream->current_table = stream->tables;
stream->current_section = stream->current_table->section;
stream->current_section_offset = 0;
stream->table_needs_send = GF_TRUE;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Generating %d sections for MPEG-4 SL packet - version number %d - extension ID %d\n", stream->pid, nb_sections, table->version_number, table_id_extension));
}
static u32 gf_m2ts_add_adaptation(GF_M2TS_Mux_Program *prog, GF_BitStream *bs, u16 pid,
Bool has_pcr, u64 pcr_time,
Bool is_rap,
u32 padding_length,
char *af_descriptors, u32 af_descriptors_size)
{
u32 adaptation_length;
adaptation_length = ADAPTATION_FLAGS_LENGTH + (has_pcr?PCR_LENGTH:0) + padding_length;
if (af_descriptors_size && af_descriptors) {
adaptation_length += ADAPTATION_EXTENSION_LENGTH_LENGTH + ADAPTATION_EXTENSION_FLAGS_LENGTH + af_descriptors_size;
}
gf_bs_write_int(bs, adaptation_length, 8);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, is_rap, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, has_pcr, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, af_descriptors_size ? 1 : 0, 1);
if (has_pcr) {
u64 PCR_base, PCR_ext;
PCR_base = pcr_time/300;
gf_bs_write_long_int(bs, PCR_base, 33);
gf_bs_write_int(bs, 0, 6);
PCR_ext = pcr_time - PCR_base*300;
gf_bs_write_long_int(bs, PCR_ext, 9);
if (prog->last_pcr > pcr_time) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Sending PCR "LLD" earlier than previous PCR "LLD" - drift %f sec - discontinuity set\n", pid, pcr_time, prog->last_pcr, (prog->last_pcr - pcr_time) /27000000.0 ));
}
prog->last_pcr = pcr_time;
#ifndef GPAC_DISABLE_LOG
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Adding adaptation field size %d - RAP %d - Padding %d - PCR "LLD"\n", pid, adaptation_length, is_rap, padding_length, pcr_time));
} else {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Adding adaptation field size %d - RAP %d - Padding %d\n", pid, adaptation_length, is_rap, padding_length));
#endif
}
if (af_descriptors_size) {
gf_bs_write_int(bs, ADAPTATION_EXTENSION_FLAGS_LENGTH + af_descriptors_size, 8);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0xF, 4);
gf_bs_write_data(bs, af_descriptors, af_descriptors_size);
}
gf_bs_write_byte(bs, 0xFF, padding_length);
return adaptation_length + ADAPTATION_LENGTH_LENGTH;
}
void gf_m2ts_mux_table_get_next_packet(GF_M2TS_Mux_Stream *stream, char *packet)
{
GF_BitStream *bs;
GF_M2TS_Mux_Table *table;
GF_M2TS_Mux_Section *section;
u32 payload_length, payload_start;
u8 adaptation_field_control = GF_M2TS_ADAPTATION_NONE;
#ifndef USE_AF_STUFFING
u32 padded_bytes=0;
#else
u32 padding_length = 0;
#endif
stream->table_needs_send = GF_FALSE;
table = stream->current_table;
if (!table) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Invalid muxer state, table is NULL!\n"));
return;
}
section = stream->current_section;
assert(section);
bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, 0x47, 8);
gf_bs_write_int(bs, 0, 1);
if (stream->current_section_offset == 0) {
gf_bs_write_int(bs, 1, 1);
} else {
gf_bs_write_int(bs, 0, 1);
}
if (!stream->current_section_offset) payload_length = 183;
else payload_length = 184;
payload_start = payload_length;
if (section->length - stream->current_section_offset >= payload_length) {
} else {
#ifdef USE_AF_STUFFING
adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD;
payload_length -= 2;
if (section->length - stream->current_section_offset >= payload_length) {
padding_length = 0;
} else {
padding_length = payload_length - section->length + stream->current_section_offset;
payload_length -= padding_length;
}
#else
padded_bytes = payload_length - section->length + stream->current_section_offset;
payload_length = section->length - stream->current_section_offset;
#endif
}
assert(payload_length + stream->current_section_offset <= section->length);
if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) {
if (!stream->continuity_counter) stream->continuity_counter=15;
else stream->continuity_counter--;
}
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, stream->pid, 13);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_int(bs, adaptation_field_control, 2);
gf_bs_write_int(bs, stream->continuity_counter, 4);
if (stream->continuity_counter < 15) stream->continuity_counter++;
else stream->continuity_counter=0;
#ifdef USE_AF_STUFFING
if (adaptation_field_control != GF_M2TS_ADAPTATION_NONE)
gf_m2ts_add_adaptation(stream->program, bs, stream->pid, 0, 0, 0, padding_length, NULL, 0);
#endif
if (!stream->current_section_offset) {
gf_bs_write_u8(bs, 0);
}
gf_bs_del(bs);
memcpy(packet+188-payload_start, section->data + stream->current_section_offset, payload_length);
stream->current_section_offset += payload_length;
#ifndef USE_AF_STUFFING
if (padded_bytes) memset(packet+188-payload_start+payload_length, 0xFF, padded_bytes);
#endif
if (stream->current_section_offset == section->length) {
stream->current_section_offset = 0;
stream->current_section = stream->current_section->next;
if (!stream->current_section) {
stream->current_table = stream->current_table->next;
if (!stream->current_table) {
if (stream->ifce) stream->refresh_rate_ms = stream->ifce->repeat_rate;
if (stream->refresh_rate_ms) {
stream->current_table = stream->tables;
gf_m2ts_time_inc(&stream->time, stream->refresh_rate_ms, 1000);
}
}
if (stream->current_table) stream->current_section = stream->current_table->section;
}
}
stream->bytes_since_last_time += 188;
}
u32 gf_m2ts_stream_process_sdt(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream)
{
if (stream->table_needs_update) {
GF_M2TS_Mux_Program *prog;
GF_BitStream *bs;
u8 *payload;
u32 size;
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_u16(bs, muxer->ts_id);
gf_bs_write_u8(bs, 0xFF);
prog = muxer->programs;
while (prog) {
u32 len = 0;
gf_bs_write_u16(bs, prog->number);
gf_bs_write_int(bs, 0xFF, 6);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 4, 3);
gf_bs_write_int(bs, 0, 1);
if (prog->name) len += (u32) strlen(prog->name);
if (prog->provider) len += (u32) strlen(prog->provider);
if (len) {
len += 3;
gf_bs_write_int(bs, len + 2, 12);
gf_bs_write_u8(bs, GF_M2TS_DVB_SERVICE_DESCRIPTOR);
gf_bs_write_u8(bs, len);
gf_bs_write_u8(bs, 0x01);
len = prog->provider ? (u32) strlen(prog->provider) : 0;
gf_bs_write_u8(bs, len);
if (prog->provider) gf_bs_write_data(bs, prog->provider, len);
len = prog->name ? (u32) strlen(prog->name) : 0;
gf_bs_write_u8(bs, len);
if (prog->name) gf_bs_write_data(bs, prog->name, len);
} else {
gf_bs_write_int(bs, 0, 12);
}
prog = prog->next;
}
gf_bs_get_content(bs, (char**)&payload, &size);
gf_bs_del(bs);
gf_m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_SDT_ACTUAL, muxer->ts_id, payload, size, GF_TRUE, GF_FALSE, GF_FALSE);
stream->table_needs_update = GF_FALSE;
stream->table_needs_send = GF_TRUE;
gf_free(payload);
}
if (stream->table_needs_send)
return 1;
if (stream->refresh_rate_ms)
return 1;
return 0;
}
u32 gf_m2ts_stream_process_pat(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream)
{
if (stream->table_needs_update) {
GF_M2TS_Mux_Program *prog;
GF_BitStream *bs;
u8 *payload;
u32 size;
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
prog = muxer->programs;
while (prog) {
gf_bs_write_u16(bs, prog->number);
gf_bs_write_int(bs, 0x7, 3);
gf_bs_write_int(bs, prog->pmt->pid, 13);
prog = prog->next;
}
gf_bs_get_content(bs, (char**)&payload, &size);
gf_bs_del(bs);
gf_m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PAT, muxer->ts_id, payload, size, GF_TRUE, GF_FALSE, GF_FALSE);
stream->table_needs_update = GF_FALSE;
stream->table_needs_send = GF_TRUE;
gf_free(payload);
}
if (stream->table_needs_send)
return 1;
if (stream->refresh_rate_ms)
return 1;
return 0;
}
u32 gf_m2ts_stream_process_pmt(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream)
{
if (stream->table_needs_update) {
GF_M2TS_Mux_Stream *es;
u8 *payload;
u32 i;
u32 length, nb_streams=0;
u32 info_length = 0, es_info_length = 0;
GF_BitStream *bs;
bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, 0x7, 3);
gf_bs_write_int(bs, stream->program->pcr->pid, 13);
gf_bs_write_int(bs, 0xF, 4);
if (stream->program->loop_descriptors) {
for (i=0; i<gf_list_count(stream->program->loop_descriptors); i++) {
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor*)gf_list_get(stream->program->loop_descriptors, i);
info_length += 2 + desc->data_len;
}
}
if (!stream->program->iod) {
gf_bs_write_int(bs, info_length, 12);
} else {
u32 len, i;
GF_ESD *esd;
GF_BitStream *bs_iod;
char *iod_data;
u32 iod_data_len;
i=0;
while (NULL != (esd = (GF_ESD*)gf_list_enum(((GF_ObjectDescriptor*)stream->program->iod)->ESDescriptors, &i))) {
GF_M2TS_Mux_Stream *es_stream = stream->program->streams;
while (es_stream) {
if (es_stream->ifce && (es_stream->ifce->stream_id==esd->ESID)) {
memcpy(esd->slConfig, es_stream->ifce->sl_config, sizeof(GF_SLConfig));
break;
}
es_stream = es_stream->next;
}
}
bs_iod = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE);
gf_odf_write_descriptor(bs_iod, stream->program->iod);
gf_bs_get_content(bs_iod, &iod_data, &iod_data_len);
gf_bs_del(bs_iod);
len = iod_data_len + 4;
gf_bs_write_int(bs, len + info_length, 12);
gf_bs_write_int(bs, GF_M2TS_MPEG4_IOD_DESCRIPTOR, 8);
len = iod_data_len + 2;
gf_bs_write_int(bs, len, 8);
gf_bs_write_int(bs, 2, 8);
gf_bs_write_int(bs, 2, 8);
gf_bs_write_data(bs, iod_data, iod_data_len);
gf_free(iod_data);
}
if (stream->program->loop_descriptors) {
for (i=0; i<gf_list_count(stream->program->loop_descriptors); i++) {
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor*)gf_list_get(stream->program->loop_descriptors, i);
gf_bs_write_int(bs, desc->tag, 8);
gf_bs_write_int(bs, desc->data_len, 8);
gf_bs_write_data(bs, desc->data, desc->data_len);
}
}
es = stream->program->streams;
while (es) {
Bool has_lang = GF_FALSE;
u8 type = es->mpeg2_stream_type;
nb_streams++;
es_info_length = 0;
switch (es->mpeg2_stream_type) {
case GF_M2TS_AUDIO_AC3:
case GF_M2TS_VIDEO_VC1:
es_info_length += 2 + 4;
type = GF_M2TS_PRIVATE_DATA;
break;
case GF_M2TS_AUDIO_EC3:
es_info_length += 2;
type = GF_M2TS_PRIVATE_DATA;
break;
}
gf_bs_write_int(bs, type, 8);
gf_bs_write_int(bs, 0x7, 3);
gf_bs_write_int(bs, es->pid, 13);
gf_bs_write_int(bs, 0xF, 4);
if (stream->program->iod && !(es->ifce->caps & GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS))
es_info_length += 4;
if (es->loop_descriptors)
{
for (i=0; i<gf_list_count(es->loop_descriptors); i++)
{
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor*)gf_list_get(es->loop_descriptors, i);
es_info_length += 2 +desc->data_len;
}
}
if (es->ifce && es->ifce->lang && (es->ifce->lang != GF_4CC('u', 'n', 'd', ' ')) ) {
es_info_length += 2 + 3;
has_lang = GF_TRUE;
}
gf_bs_write_int(bs, es_info_length, 12);
if (stream->program->iod && !(es->ifce->caps & GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS)) {
gf_bs_write_int(bs, GF_M2TS_MPEG4_SL_DESCRIPTOR, 8);
gf_bs_write_int(bs, 2, 8);
gf_bs_write_int(bs, es->ifce->stream_id, 16);
}
if (has_lang) {
gf_bs_write_int(bs, GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR, 8);
gf_bs_write_int(bs, 3, 8);
gf_bs_write_int(bs, (es->ifce->lang>>24) & 0xFF, 8);
gf_bs_write_int(bs, (es->ifce->lang>>16) & 0xFF, 8);
gf_bs_write_int(bs, es->ifce->lang & 0xFF, 8);
}
switch (es->mpeg2_stream_type) {
case GF_M2TS_AUDIO_AC3:
gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8);
gf_bs_write_int(bs, 4, 8);
gf_bs_write_int(bs, 'A', 8);
gf_bs_write_int(bs, 'C', 8);
gf_bs_write_int(bs, '-', 8);
gf_bs_write_int(bs, '3', 8);
break;
case GF_M2TS_VIDEO_VC1:
gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8);
gf_bs_write_int(bs, 4, 8);
gf_bs_write_int(bs, 'V', 8);
gf_bs_write_int(bs, 'C', 8);
gf_bs_write_int(bs, '-', 8);
gf_bs_write_int(bs, '1', 8);
break;
case GF_M2TS_AUDIO_EC3:
gf_bs_write_int(bs, GF_M2TS_DVB_EAC3_DESCRIPTOR, 8);
gf_bs_write_int(bs, 0, 8);
break;
}
if (es->loop_descriptors)
{
for (i=0; i<gf_list_count(es->loop_descriptors); i++)
{
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor *)gf_list_get(es->loop_descriptors, i);
gf_bs_write_int(bs, desc->tag, 8);
gf_bs_write_int(bs, desc->data_len, 8);
gf_bs_write_data(bs, desc->data, desc->data_len);
}
}
es = es->next;
}
gf_bs_get_content(bs, (char**)&payload, &length);
gf_bs_del(bs);
gf_m2ts_mux_table_update(stream, GF_M2TS_TABLE_ID_PMT, stream->program->number, payload, length, GF_TRUE, GF_FALSE, GF_FALSE);
stream->table_needs_update = GF_FALSE;
stream->table_needs_send = GF_TRUE;
gf_free(payload);
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Updating PMT - Program Number %d - %d streams - size %d%s\n", stream->pid, stream->program->number, nb_streams, length, stream->program->iod ? " - MPEG-4 Systems detected":""));
}
if (stream->table_needs_send)
return 1;
if (stream->refresh_rate_ms)
return 1;
return 0;
}
static void gf_m2ts_remap_timestamps_for_pes(GF_M2TS_Mux_Stream *stream, u32 pck_flags, u64 *dts, u64 *cts, u32 *duration)
{
u64 pcr_offset;
if (*dts > *cts) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: DTS "LLD" is greater than CTS "LLD" (likel ISOBMF CTTSv1 input) - adjusting to CTS\n", stream->pid, *dts, *cts));
*dts = *cts;
}
if (stream->ts_scale.den) {
*cts = (u64)( *cts * stream->ts_scale.num / stream->ts_scale.den ) ;
*dts = (u64)( *dts * stream->ts_scale.num / stream->ts_scale.den ) ;
if (duration) *duration = (u32)( *duration * stream->ts_scale.num / stream->ts_scale.den ) ;
}
if (!stream->program->initial_ts_set) {
u32 nb_bits = (u32) (stream->program->mux->tot_pck_sent - stream->program->num_pck_at_pcr_init) * 1504;
u32 nb_ticks = 90000*nb_bits / stream->program->mux->bit_rate;
stream->program->initial_ts = *dts;
if (stream->program->initial_ts > nb_ticks)
stream->program->initial_ts -= nb_ticks;
else
stream->program->initial_ts = 0;
stream->program->initial_ts_set = GF_TRUE;
}
else if (*dts < stream->program->initial_ts) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: DTS "LLD" is less than initial DTS "LLD" - adjusting\n", stream->pid, *dts, stream->program->initial_ts));
stream->program->initial_ts = *dts;
}
else if (*dts < stream->last_dts) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: DTS "LLD" is less than last sent DTS "LLD"\n", stream->pid, *dts, stream->last_dts));
stream->last_dts = *dts;
} else {
stream->last_dts = *dts;
}
*cts += stream->program->pcr_offset;
*dts += stream->program->pcr_offset;
pcr_offset = stream->program->pcr_init_time/300;
*cts = *cts - stream->program->initial_ts + pcr_offset;
*dts = *dts - stream->program->initial_ts + pcr_offset;
}
static void id3_tag_create(char **input, u32 *len)
{
GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_u8(bs, 'I');
gf_bs_write_u8(bs, 'D');
gf_bs_write_u8(bs, '3');
gf_bs_write_u8(bs, 4);
gf_bs_write_u8(bs, 0);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0x1F, 5);
gf_bs_write_u32(bs, GF_4CC('T','X','X','X'));
gf_bs_write_u32(bs, *len);
gf_bs_write_u8(bs, 0);
gf_bs_write_u8(bs, 0);
gf_bs_write_data(bs, *input, *len);
gf_free(*input);
gf_bs_get_content(bs, input, len);
gf_bs_del(bs);
}
static Bool gf_m2ts_adjust_next_stream_time_for_pcr(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream)
{
u32 pck_diff;
s32 us_diff;
GF_M2TS_Time next_pcr_time, stream_time;
if (!muxer->enable_forced_pcr) return 1;
if (!muxer->bit_rate) return 1;
next_pcr_time = stream->program->ts_time_at_pcr_init;
pck_diff = (u32) (stream->program->nb_pck_last_pcr - stream->program->num_pck_at_pcr_init);
gf_m2ts_time_inc(&next_pcr_time, pck_diff*1504, stream->program->mux->bit_rate);
gf_m2ts_time_inc(&next_pcr_time, stream->program->mux->pcr_update_ms, 1000);
stream_time = stream->pcr_only_mode ? stream->next_time : stream->time;
us_diff = gf_m2ts_time_diff_us(&next_pcr_time, &stream_time);
if (us_diff > 0) {
if (!stream->pcr_only_mode) {
stream->pcr_only_mode = GF_TRUE;
stream->next_time = stream->time;
}
stream->time = next_pcr_time;
us_diff = gf_m2ts_time_diff_us(&stream->program->mux->time, &stream->time);
if (us_diff>1000)
return 0;
} else if (stream->pcr_only_mode) {
stream->pcr_only_mode = GF_FALSE;
stream->time = stream->next_time;
}
return 1;
}
static u32 gf_m2ts_stream_process_pes(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream)
{
u64 time_inc;
Bool ret = GF_FALSE;
if (stream->mpeg2_stream_type==GF_M2TS_SYSTEMS_MPEG4_SECTIONS) {
if (stream->table_needs_send)
return stream->scheduling_priority;
if (stream->current_section && (stream->current_section_offset || stream->current_section!=stream->current_table->section))
return stream->scheduling_priority;
if (stream->ifce->repeat_rate && stream->tables)
ret = stream->program->pcr_init_time ? stream->scheduling_priority : GF_FALSE;
}
else if (stream->curr_pck.data_len && stream->pck_offset < stream->curr_pck.data_len) {
if ((stream == stream->program->pcr) && stream->pcr_only_mode) {
if (! gf_m2ts_adjust_next_stream_time_for_pcr(muxer, stream)) {
return 0;
}
}
return stream->scheduling_priority;
}
if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) {
if (stream->curr_pck.data_len) {
if (stream->discard_data) gf_free(stream->curr_pck.data);
stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_RELEASE, NULL);
}
stream->pck_offset = 0;
stream->curr_pck.data_len = 0;
stream->discard_data = GF_FALSE;
if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) return ret;
assert( stream->ifce->input_ctrl);
stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_PULL, &stream->curr_pck);
} else {
GF_M2TS_Packet *curr_pck;
if (!stream->pck_first && (stream->ifce->caps & GF_ESI_STREAM_IS_OVER))
return ret;
if (stream->ifce->input_ctrl) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL);
gf_mx_p(stream->mx);
stream->pck_offset = 0;
stream->curr_pck.data_len = 0;
curr_pck = stream->pck_first;
if (!curr_pck) {
gf_mx_v(stream->mx);
return ret;
}
stream->curr_pck.cts = curr_pck->cts;
stream->curr_pck.data = curr_pck->data;
stream->curr_pck.data_len = curr_pck->data_len;
stream->curr_pck.dts = curr_pck->dts;
stream->curr_pck.duration = curr_pck->duration;
stream->curr_pck.flags = curr_pck->flags;
stream->curr_pck.mpeg2_af_descriptors = curr_pck->mpeg2_af_descriptors;
stream->curr_pck.mpeg2_af_descriptors_size = curr_pck->mpeg2_af_descriptors_size;
stream->pck_first = curr_pck->next;
gf_free(curr_pck);
stream->discard_data = GF_TRUE;
gf_mx_v(stream->mx);
}
if (!(stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS))
stream->curr_pck.dts = stream->curr_pck.cts;
if (!stream->program->pcr_init_time_set) {
if (stream==stream->program->pcr) {
if (stream->program->mux->init_pcr_value) {
stream->program->pcr_init_time = stream->program->mux->init_pcr_value-1;
} else {
while (!stream->program->pcr_init_time)
stream->program->pcr_init_time = gf_rand();
}
stream->program->pcr_init_time_set = GF_TRUE;
stream->program->ts_time_at_pcr_init = muxer->time;
stream->program->num_pck_at_pcr_init = muxer->tot_pck_sent;
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Initializing PCR for program number %d: PCR %d - mux time %d:%09d\n", stream->pid, stream->program->number, stream->program->pcr_init_time, muxer->time.sec, muxer->time.nanosec));
} else {
if (stream->discard_data) gf_free(stream->curr_pck.data);
stream->curr_pck.data = NULL;
stream->curr_pck.data_len = 0;
stream->pck_offset = 0;
return 0;
}
}
switch (stream->mpeg2_stream_type) {
case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
stream->sl_header.accessUnitStartFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_START) ? 1 : 0;
stream->sl_header.accessUnitEndFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_END) ? 1 : 0;
#if 0
assert(stream->sl_header.accessUnitLength + stream->curr_pck.data_len < 65536);
stream->sl_header.accessUnitLength += stream->curr_pck.data_len;
#endif
stream->sl_header.randomAccessPointFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_RAP) ? 1: 0;
stream->sl_header.compositionTimeStampFlag = (stream->curr_pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0;
stream->sl_header.compositionTimeStamp = stream->curr_pck.cts;
stream->sl_header.decodingTimeStampFlag = (stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS) ? 1: 0;
stream->sl_header.decodingTimeStamp = stream->curr_pck.dts;
gf_m2ts_mux_table_update_mpeg4(stream, stream->table_id, muxer->ts_id, stream->curr_pck.data, stream->curr_pck.data_len, GF_TRUE, GF_FALSE, (stream->curr_pck.flags & GF_ESI_DATA_REPEAT) ? GF_FALSE : GF_TRUE, GF_FALSE);
if (!(stream->ifce->caps & GF_ESI_AU_PULL_CAP)) {
gf_free(stream->curr_pck.data);
stream->curr_pck.data = NULL;
stream->curr_pck.data_len = 0;
}
break;
case GF_M2TS_SYSTEMS_MPEG4_PES:
{
char *src_data;
u32 src_data_len;
stream->sl_header.accessUnitStartFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_START) ? 1 : 0;
stream->sl_header.accessUnitEndFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_END) ? 1 : 0;
#if 0
assert(stream->sl_header.accessUnitLength + stream->curr_pck.data_len < 65536);
stream->sl_header.accessUnitLength += stream->curr_pck.data_len;
#endif
stream->sl_header.randomAccessPointFlag = (stream->curr_pck.flags & GF_ESI_DATA_AU_RAP) ? 1: 0;
stream->sl_header.compositionTimeStampFlag = (stream->curr_pck.flags & GF_ESI_DATA_HAS_CTS) ? 1 : 0;
stream->sl_header.compositionTimeStamp = stream->curr_pck.cts;
stream->sl_header.decodingTimeStampFlag = (stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS) ? 1: 0;
stream->sl_header.decodingTimeStamp = stream->curr_pck.dts;
src_data = stream->curr_pck.data;
src_data_len = stream->curr_pck.data_len;
stream->curr_pck.data_len = 0;
stream->curr_pck.data = NULL;
gf_sl_packetize(stream->ifce->sl_config, &stream->sl_header, src_data, src_data_len, &stream->curr_pck.data, &stream->curr_pck.data_len);
if (!(stream->ifce->caps & GF_ESI_AU_PULL_CAP)) {
gf_free(src_data);
}
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Encapsulating MPEG-4 SL Data (%p - %p) on PES - SL Header size %d\n", stream->pid, src_data, stream->curr_pck.data, stream->curr_pck.data_len - src_data_len));
stream->discard_data = GF_TRUE;
}
break;
case GF_M2TS_AUDIO_LATM_AAC:
{
u32 size, next_time;
GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, 0x2B7, 11);
gf_bs_write_int(bs, 0, 13);
next_time = gf_sys_clock();
if (stream->ifce->decoder_config && (stream->last_aac_time + stream->ifce->repeat_rate < next_time)) {
GF_M4ADecSpecInfo cfg;
stream->last_aac_time = next_time;
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 0, 6);
gf_bs_write_int(bs, 0, 4);
gf_bs_write_int(bs, 0, 3);
gf_m4a_get_config(stream->ifce->decoder_config, stream->ifce->decoder_config_size, &cfg);
gf_m4a_write_config_bs(bs, &cfg);
gf_bs_write_int(bs, 0, 3);
gf_bs_write_int(bs, 0, 8);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
} else {
gf_bs_write_int(bs, 1, 1);
}
size = stream->curr_pck.data_len;
while (1) {
if (size>=255) {
gf_bs_write_int(bs, 255, 8);
size -= 255;
} else {
gf_bs_write_int(bs, size, 8);
break;
}
}
stream->reframe_overhead = stream->curr_pck.data_len;
gf_bs_write_data(bs, stream->curr_pck.data, stream->curr_pck.data_len);
gf_bs_align(bs);
gf_free(stream->curr_pck.data);
gf_bs_get_content(bs, &stream->curr_pck.data, &stream->curr_pck.data_len);
gf_bs_del(bs);
stream->reframe_overhead = stream->curr_pck.data_len - stream->reframe_overhead;
size = stream->curr_pck.data_len - 2;
stream->curr_pck.data[1] |= (size>>8) & 0x1F;
stream->curr_pck.data[2] = (size) & 0xFF;
stream->discard_data = GF_TRUE;
}
break;
case GF_M2TS_AUDIO_AAC:
if (stream->ifce->decoder_config) {
GF_M4ADecSpecInfo cfg;
GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_m4a_get_config(stream->ifce->decoder_config, stream->ifce->decoder_config_size, &cfg);
if (cfg.base_object_type>=5) cfg.base_object_type = GF_M4A_AAC_LC;
gf_bs_write_int(bs, 0xFFF, 12);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, cfg.base_object_type-1, 2);
gf_bs_write_int(bs, cfg.base_sr_index, 4);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, cfg.nb_chan, 3);
gf_bs_write_int(bs, 0, 4);
gf_bs_write_int(bs, 7+stream->curr_pck.data_len, 13);
gf_bs_write_int(bs, 0x7FF, 11);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_data(bs, stream->curr_pck.data, stream->curr_pck.data_len);
gf_bs_align(bs);
gf_free(stream->curr_pck.data);
gf_bs_get_content(bs, &stream->curr_pck.data, &stream->curr_pck.data_len);
gf_bs_del(bs);
stream->reframe_overhead = 7;
}
stream->discard_data = GF_TRUE;
break;
case GF_M2TS_METADATA_PES:
case GF_M2TS_METADATA_ID3_HLS:
{
id3_tag_create(&stream->curr_pck.data, &stream->curr_pck.data_len);
stream->discard_data = GF_TRUE;
}
break;
}
if (stream->start_pes_at_rap && (stream->curr_pck.flags & GF_ESI_DATA_AU_RAP)
) {
stream->program->mux->force_pat_pmt_state = GF_SEG_BOUNDARY_FORCE_PAT;
stream->program->mux->force_pat = GF_TRUE;
}
gf_m2ts_remap_timestamps_for_pes(stream, stream->curr_pck.flags, &stream->curr_pck.dts, &stream->curr_pck.cts, &stream->curr_pck.duration);
stream->time = stream->program->ts_time_at_pcr_init;
time_inc = stream->curr_pck.dts - stream->program->pcr_offset;
gf_m2ts_time_inc(&stream->time, time_inc, 90000);
if (stream == stream->program->pcr) {
gf_m2ts_adjust_next_stream_time_for_pcr(muxer, stream);
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Next data schedule for %d:%09d - mux time %d:%09d\n", stream->pid, stream->time.sec, stream->time.nanosec, muxer->time.sec, muxer->time.nanosec));
if (!stream->last_br_time) {
stream->last_br_time = stream->curr_pck.dts + 1;
stream->bytes_since_last_time = 0;
stream->pes_since_last_time = 0;
} else {
u32 time_diff = (u32) (stream->curr_pck.dts + 1 - stream->last_br_time );
if ((stream->pes_since_last_time > 4) && (time_diff >= BITRATE_UPDATE_WINDOW)) {
u32 bitrate;
u64 r = 8*stream->bytes_since_last_time;
r*=90000;
bitrate = (u32) (r / time_diff);
if (stream->program->mux->fixed_rate || (stream->bit_rate < bitrate)) {
stream->bit_rate = bitrate;
stream->program->mux->needs_reconfig = GF_TRUE;
}
stream->last_br_time = 0;
stream->bytes_since_last_time = 0;
stream->pes_since_last_time = 0;
}
}
stream->pes_since_last_time ++;
return stream->scheduling_priority;
}
static GFINLINE u64 gf_m2ts_get_pcr(GF_M2TS_Mux_Stream *stream)
{
u64 pcr;
if (stream->program->mux->fixed_rate ) {
Double abs_pcr = (Double) (stream->program->mux->tot_pck_sent - stream->program->num_pck_at_pcr_init);
abs_pcr *= 27000000;
abs_pcr *= 1504;
abs_pcr /= stream->program->mux->bit_rate;
pcr = (u64) abs_pcr + stream->program->pcr_init_time;
}
else {
pcr = (stream->curr_pck.dts - stream->program->pcr_offset) * 300;
}
return pcr;
}
void gf_m2ts_stream_update_data_following(GF_M2TS_Mux_Stream *stream)
{
Bool ignore_next = GF_FALSE;
stream->next_payload_size = 0;
stream->next_next_payload_size = 0;
stream->next_pck_flags = 0;
stream->copy_from_next_packets = 0;
if (stream->force_single_au) return;
if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) {
GF_ESIPacket test_pck;
test_pck.data_len = 0;
stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_PULL, &test_pck);
if (test_pck.data_len) {
stream->next_payload_size = test_pck.data_len;
stream->next_pck_flags = test_pck.flags;
stream->next_pck_cts = test_pck.cts;
stream->next_pck_dts = test_pck.dts;
}
} else {
if (!stream->pck_first && stream->ifce->input_ctrl) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL);
if (stream->pck_first) {
stream->next_payload_size = stream->pck_first->data_len;
stream->next_pck_cts = stream->pck_first->cts;
stream->next_pck_dts = stream->pck_first->dts;
stream->next_pck_flags = stream->pck_first->flags;
if (!stream->pck_first->next && stream->ifce->input_ctrl) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL);
if (stream->pck_first->next) {
stream->next_next_payload_size = stream->pck_first->next->data_len;
}
}
}
if (stream->start_pes_at_rap && (stream->next_pck_flags & GF_ESI_DATA_AU_RAP) ) {
ignore_next = GF_TRUE;
}
else if (stream->program->mux->force_pat_pmt_state) {
ignore_next = GF_TRUE;
}
if (ignore_next) {
stream->next_payload_size = 0;
stream->next_pck_cts = 0;
stream->next_pck_dts = 0;
stream->next_pck_flags = 0;
}
if (stream->next_payload_size) {
stream->next_payload_size += stream->reframe_overhead;
if (stream->next_next_payload_size)
stream->next_next_payload_size += stream->reframe_overhead;
gf_m2ts_remap_timestamps_for_pes(stream, stream->next_pck_flags, &stream->next_pck_dts, &stream->next_pck_cts, NULL);
if (!(stream->next_pck_flags & GF_ESI_DATA_HAS_DTS))
stream->next_pck_dts = stream->next_pck_cts;
}
}
Bool gf_m2ts_stream_compute_pes_length(GF_M2TS_Mux_Stream *stream, u32 payload_length)
{
assert(stream->pes_data_remain==0);
stream->pes_data_len = stream->curr_pck.data_len - stream->pck_offset;
stream->copy_from_next_packets = 0;
if (stream->next_payload_size) {
u32 pck_size = stream->curr_pck.data_len - stream->pck_offset;
u32 ts_bytes = payload_length;
while (ts_bytes < pck_size) {
ts_bytes += 184;
}
if (stream->prevent_two_au_start_in_pes && !stream->pck_offset) {
if (ts_bytes>184)
ts_bytes -= 184;
else
ts_bytes = pck_size;
}
else if (stream->next_payload_size) {
while (ts_bytes < pck_size + stream->next_payload_size) {
ts_bytes += 184;
}
if ((stream->prevent_two_au_start_in_pes && (ts_bytes>pck_size + stream->next_payload_size))
|| !stream->next_next_payload_size
) {
if (ts_bytes>184)
ts_bytes -= 184;
else
ts_bytes = pck_size + stream->next_payload_size;
}
}
if (ts_bytes >= pck_size) {
stream->copy_from_next_packets = ts_bytes - pck_size;
} else {
u32 skipped = pck_size-ts_bytes;
if (stream->pes_data_len > skipped)
stream->pes_data_len -= skipped;
}
if (stream->min_bytes_copy_from_next && stream->copy_from_next_packets) {
if (stream->copy_from_next_packets < stream->min_bytes_copy_from_next) {
stream->copy_from_next_packets = 0;
stream->next_payload_size = 0;
stream->next_pck_flags = 0;
return GF_FALSE;
}
if ((stream->copy_from_next_packets > stream->next_payload_size)
&& (stream->copy_from_next_packets - stream->next_payload_size < stream->min_bytes_copy_from_next)
) {
stream->copy_from_next_packets = stream->next_payload_size;
}
}
if (stream->pck_offset && !stream->copy_from_next_packets && stream->next_payload_size) {
stream->copy_from_next_packets = 0;
stream->next_payload_size = 0;
stream->next_pck_flags = 0;
return GF_FALSE;
}
if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) {
#if 0
while (stream->copy_from_next_packets > stream->next_payload_size) {
if (stream->copy_from_next_packets < 184) {
stream->copy_from_next_packets = 0;
break;
}
stream->copy_from_next_packets -= 184;
}
#endif
stream->pes_data_len += stream->next_payload_size;
} else {
stream->pes_data_len += stream->copy_from_next_packets;
}
}
stream->pes_data_remain = stream->pes_data_len;
return GF_TRUE;
}
static u32 gf_m2ts_stream_get_pes_header_length(GF_M2TS_Mux_Stream *stream)
{
u32 hdr_len, flags;
flags = stream->pck_offset ? stream->next_pck_flags : stream->curr_pck.flags;
if (stream->pes_data_remain) return 0;
hdr_len = 9;
if ( flags & GF_ESI_DATA_AU_START) {
if (flags & GF_ESI_DATA_HAS_CTS) hdr_len += 5;
if (flags & GF_ESI_DATA_HAS_DTS) hdr_len += 5;
}
return hdr_len;
}
u32 gf_m2ts_stream_add_pes_header(GF_BitStream *bs, GF_M2TS_Mux_Stream *stream)
{
u64 t, dts, cts;
u32 pes_len;
Bool use_pts, use_dts;
gf_bs_write_int(bs, 0x1, 24);
gf_bs_write_u8(bs, stream->mpeg2_stream_id);
if (stream->pck_offset && stream->copy_from_next_packets) {
use_pts = (stream->next_pck_flags & GF_ESI_DATA_HAS_CTS) ? GF_TRUE : GF_FALSE;
use_dts = (stream->next_pck_flags & GF_ESI_DATA_HAS_DTS) ? GF_TRUE : GF_FALSE;
dts = stream->next_pck_dts;
cts = stream->next_pck_cts;
}
else if (stream->pck_offset) {
use_pts = use_dts = GF_FALSE;
dts = cts = 0;
} else {
use_pts = (stream->curr_pck.flags & GF_ESI_DATA_HAS_CTS) ? GF_TRUE : GF_FALSE;
use_dts = (stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS) ? GF_TRUE : GF_FALSE;
dts = stream->curr_pck.dts;
cts = stream->curr_pck.cts;
}
assert(stream->pes_data_len);
pes_len = stream->pes_data_len + 3;
if (use_pts) pes_len += 5;
if (use_dts) pes_len += 5;
if (pes_len>0xFFFF) pes_len = 0;
gf_bs_write_int(bs, pes_len, 16);
gf_bs_write_int(bs, 0x2, 2);
gf_bs_write_int(bs, 0x0, 2);
gf_bs_write_int(bs, 0x0, 1);
gf_bs_write_int(bs, stream->pck_offset ? 0 : 1, 1);
gf_bs_write_int(bs, 0x0, 1);
gf_bs_write_int(bs, 0x0, 1);
gf_bs_write_int(bs, use_pts, 1);
gf_bs_write_int(bs, use_dts, 1);
gf_bs_write_int(bs, 0x0, 6);
gf_bs_write_int(bs, use_dts*5+use_pts*5, 8);
if (use_pts) {
gf_bs_write_int(bs, use_dts ? 0x3 : 0x2, 4);
t = ((cts >> 30) & 0x7);
gf_bs_write_long_int(bs, t, 3);
gf_bs_write_int(bs, 1, 1);
t = ((cts >> 15) & 0x7fff);
gf_bs_write_long_int(bs, t, 15);
gf_bs_write_int(bs, 1, 1);
t = cts & 0x7fff;
gf_bs_write_long_int(bs, t, 15);
gf_bs_write_int(bs, 1, 1);
}
if (use_dts) {
gf_bs_write_int(bs, 0x1, 4);
t = ((dts >> 30) & 0x7);
gf_bs_write_long_int(bs, t, 3);
gf_bs_write_int(bs, 1, 1);
t = ((dts >> 15) & 0x7fff);
gf_bs_write_long_int(bs, t, 15);
gf_bs_write_int(bs, 1, 1);
t = dts & 0x7fff;
gf_bs_write_long_int(bs, t, 15);
gf_bs_write_int(bs, 1, 1);
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: Adding PES header at PCR "LLD" - has PTS %d ("LLU") - has DTS %d ("LLU") - Payload length %d\n", stream->pid, gf_m2ts_get_pcr(stream)/300, use_pts, cts, use_dts, dts, pes_len));
return pes_len+4;
}
void gf_m2ts_mux_pes_get_next_packet(GF_M2TS_Mux_Stream *stream, char *packet)
{
GF_BitStream *bs;
Bool needs_pcr, first_pass;
u32 adaptation_field_control, payload_length, payload_to_copy, padding_length, hdr_len, pos, copy_next;
assert(stream->pid);
bs = gf_bs_new(packet, 188, GF_BITSTREAM_WRITE);
if (stream->pcr_only_mode) {
payload_length = 184 - 8;
needs_pcr = GF_TRUE;
adaptation_field_control = GF_M2TS_ADAPTATION_ONLY;
hdr_len = 0;
} else {
hdr_len = gf_m2ts_stream_get_pes_header_length(stream);
first_pass = GF_TRUE;
while (1) {
if (hdr_len) {
if (first_pass)
gf_m2ts_stream_update_data_following(stream);
hdr_len = gf_m2ts_stream_get_pes_header_length(stream);
}
adaptation_field_control = GF_M2TS_ADAPTATION_NONE;
payload_length = 184 - hdr_len;
needs_pcr = GF_FALSE;
if (stream == stream->program->pcr) {
if (hdr_len)
needs_pcr = GF_TRUE;
if (stream->program->mux->force_pat_pmt_state == GF_SEG_BOUNDARY_FORCE_PCR) {
stream->program->mux->force_pat_pmt_state = GF_SEG_BOUNDARY_NONE;
needs_pcr = GF_TRUE;
}
if (!needs_pcr && (stream->program->mux->real_time || stream->program->mux->fixed_rate) ) {
u64 clock;
u32 diff;
if (stream->program->mux->fixed_rate) {
clock = 1 + stream->program->mux->tot_pck_sent - stream->program->nb_pck_last_pcr;
clock *= 1504*1000000;
clock /= stream->program->mux->bit_rate;
if (clock >= 500 + stream->program->mux->pcr_update_ms*1000) {
needs_pcr = GF_TRUE;
}
}
if (!needs_pcr && stream->program->mux->real_time) {
clock = gf_sys_clock_high_res();
diff = (u32) (clock - stream->program->sys_clock_at_last_pcr);
if (diff >= 100 + stream->program->mux->pcr_update_ms*1000) {
needs_pcr = GF_TRUE;
}
}
}
}
if (needs_pcr) {
payload_length -= 8;
adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD;
}
if (hdr_len && stream->curr_pck.mpeg2_af_descriptors) {
if (adaptation_field_control == GF_M2TS_ADAPTATION_NONE) {
payload_length -= 2;
adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD;
}
payload_length -= 2 + stream->curr_pck.mpeg2_af_descriptors_size;
}
if (hdr_len) {
assert(!stream->pes_data_remain);
if (! gf_m2ts_stream_compute_pes_length(stream, payload_length)) {
first_pass = GF_FALSE;
continue;
}
assert(stream->pes_data_remain==stream->pes_data_len);
}
break;
}
}
copy_next = stream->copy_from_next_packets;
payload_to_copy = stream->curr_pck.data_len - stream->pck_offset;
if (payload_to_copy >= stream->pes_data_remain) {
payload_to_copy = stream->pes_data_remain;
copy_next = 0;
}
if (payload_to_copy >= payload_length) {
padding_length = 0;
payload_to_copy = payload_length;
}
else if (payload_to_copy + copy_next >= payload_length) {
padding_length = 0;
}
else {
if (!needs_pcr) {
if (adaptation_field_control == GF_M2TS_ADAPTATION_NONE) {
payload_length -= 2;
adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD;
}
}
if (payload_length < payload_to_copy + copy_next) {
padding_length = 10;
payload_length -= padding_length;
if (payload_to_copy > payload_length)
payload_to_copy = payload_length;
} else {
padding_length = payload_length - payload_to_copy - copy_next;
payload_length -= padding_length;
}
}
#ifndef GPAC_DISABLE_LOG
if (hdr_len && gf_log_tool_level_on(GF_LOG_CONTAINER, GF_LOG_DEBUG) ) {
u64 pcr = (s64) gf_m2ts_get_pcr(stream)/300;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Start sending PES: PID %d - %d bytes - DTS "LLD" PCR "LLD" (diff %d) - stream time %d:%09d - mux time %d:%09d (%d us ahead of mux time)\n",
stream->pid, stream->curr_pck.data_len, stream->curr_pck.dts, pcr, (s64) stream->curr_pck.dts - (s64) pcr,
stream->time.sec, stream->time.nanosec, stream->program->mux->time.sec, stream->program->mux->time.nanosec,
gf_m2ts_time_diff_us(&stream->program->mux->time, &stream->time)
));
}
#endif
if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) {
if (!stream->continuity_counter) stream->continuity_counter=15;
else stream->continuity_counter--;
}
gf_bs_write_int(bs, 0x47, 8);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, hdr_len ? 1 : 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, stream->pid, 13);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_int(bs, adaptation_field_control, 2);
gf_bs_write_int(bs, stream->continuity_counter, 4);
if (stream->continuity_counter < 15) stream->continuity_counter++;
else stream->continuity_counter=0;
if (adaptation_field_control != GF_M2TS_ADAPTATION_NONE) {
Bool is_rap = GF_FALSE;
u64 pcr = 0;
if (needs_pcr) {
u64 now = gf_sys_clock_high_res();
pcr = gf_m2ts_get_pcr(stream);
if (stream->program->mux->real_time || stream->program->mux->fixed_rate) {
u64 clock;
clock = stream->program->mux->tot_pck_sent - stream->program->nb_pck_last_pcr;
clock *= 1504000000;
clock /= stream->program->mux->bit_rate;
if (clock > 2000 + stream->program->mux->pcr_update_ms*1000) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] PCR sent %d us later than requested PCR send rate %d ms\n", (s32) (clock - stream->program->mux->pcr_update_ms*1000), stream->program->mux->pcr_update_ms ));
}
if (stream->program->mux->real_time) {
u32 diff = (s32) (now - stream->program->sys_clock_at_last_pcr);
if (diff > 5000 + 2*stream->program->mux->pcr_update_ms*1000 ) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sending PCR %d us too late (PCR send rate %d ms)\n", (u32) (diff - stream->program->mux->pcr_update_ms*1000), stream->program->mux->pcr_update_ms ));
}
}
}
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Inserted PCR "LLD" (%d @90kHz) at mux time %d:%09d\n", pcr, (u32) (pcr/300), stream->program->mux->time.sec, stream->program->mux->time.nanosec ));
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] PCR diff to STB in ms %d - sys clock diff in ms %d - DTS diff %d\n", (u32) (pcr - stream->program->last_pcr) / 27000, now - stream->program->sys_clock_at_last_pcr, (stream->curr_pck.dts - stream->program->last_dts)/90));
stream->program->sys_clock_at_last_pcr = now;
stream->program->last_dts = stream->curr_pck.dts;
stream->program->nb_pck_last_pcr = stream->program->mux->tot_pck_sent;
}
is_rap = (hdr_len && (stream->curr_pck.flags & GF_ESI_DATA_AU_RAP) ) ? GF_TRUE : GF_FALSE;
gf_m2ts_add_adaptation(stream->program, bs, stream->pid, needs_pcr, pcr, is_rap, padding_length, hdr_len ? stream->curr_pck.mpeg2_af_descriptors : NULL, hdr_len ? stream->curr_pck.mpeg2_af_descriptors_size : 0);
if (stream->curr_pck.mpeg2_af_descriptors) {
gf_free(stream->curr_pck.mpeg2_af_descriptors);
stream->curr_pck.mpeg2_af_descriptors = NULL;
stream->curr_pck.mpeg2_af_descriptors_size = 0;
}
if (padding_length)
stream->program->mux->tot_pes_pad_bytes += padding_length;
}
if (hdr_len) gf_m2ts_stream_add_pes_header(bs, stream);
pos = (u32) gf_bs_get_position(bs);
gf_bs_del(bs);
if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) {
return;
}
assert(stream->curr_pck.data_len - stream->pck_offset >= payload_to_copy);
memcpy(packet+pos, stream->curr_pck.data + stream->pck_offset, payload_to_copy);
stream->pck_offset += payload_to_copy;
assert(stream->pes_data_remain >= payload_to_copy);
stream->pes_data_remain -= payload_to_copy;
gf_m2ts_time_inc(&stream->time, 1504, stream->program->mux->bit_rate);
if (stream->pck_offset == stream->curr_pck.data_len) {
u64 pcr = gf_m2ts_get_pcr(stream)/300;
if (stream->program->mux->real_time && !stream->program->mux->fixed_rate && gf_m2ts_time_less(&stream->time, &stream->program->mux->time) ) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sent PES TOO LATE: PID %d - DTS "LLD" - PCR "LLD" - stream time %d:%09d - mux time %d:%09d - current mux rate %d\n",
stream->pid, stream->curr_pck.dts, pcr,
stream->time.sec, stream->time.nanosec, stream->program->mux->time.sec, stream->program->mux->time.nanosec,
stream->program->mux->bit_rate
));
} else if (stream->curr_pck.dts < pcr) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sent PES %d us TOO LATE: PID %d - DTS "LLD" - size %d\n\tPCR "LLD" - stream time %d:%09d - mux time %d:%09d \n",
(pcr - stream->curr_pck.dts)*100/9, stream->pid, stream->curr_pck.dts, stream->curr_pck.data_len, pcr,
stream->time.sec, stream->time.nanosec, stream->program->mux->time.sec, stream->program->mux->time.nanosec
));
} else if (stream->curr_pck.cts < pcr) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sent PES %d us TOO LATE: PID %d - DTS "LLD" - size %d\n\tPCR "LLD" - stream time %d:%09d - mux time %d:%09d \n",
pcr - stream->curr_pck.dts, stream->pid, stream->curr_pck.dts, stream->curr_pck.data_len, pcr,
stream->time.sec, stream->time.nanosec, stream->program->mux->time.sec, stream->program->mux->time.nanosec
));
} else {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sent PES: PID %d - DTS "LLD" - PCR "LLD" - stream time %d:%09d - mux time %d:%09d \n",
stream->pid, stream->curr_pck.dts, pcr,
stream->time.sec, stream->time.nanosec, stream->program->mux->time.sec, stream->program->mux->time.nanosec
));
}
if (stream->discard_data) gf_free(stream->curr_pck.data);
stream->curr_pck.data = NULL;
stream->curr_pck.data_len = 0;
stream->pck_offset = 0;
#if 0
if (gf_m2ts_time_less(&stream->program->mux->time, &stream->time) ) {
s32 drift = gf_m2ts_time_diff_us(&stream->program->mux->time, &stream->time);
GF_LOG( (drift>1000) ? GF_LOG_WARNING : GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] PES PID %d sent %d us too late\n", stream->pid, drift) );
}
#endif
if (stream->copy_from_next_packets) {
u32 copy_next;
pos += payload_to_copy;
copy_next = payload_length - payload_to_copy;
while (stream->pes_data_remain) {
u32 remain = 0;
Bool res = stream->process(stream->program->mux, stream);
if (!res) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Not enough data to fill current PES (PID %d) - filling with 0xFF\n", stream->pid) );
memset(packet+pos, 0xFF, copy_next);
if (stream->copy_from_next_packets > copy_next) {
stream->copy_from_next_packets -= copy_next;
} else {
stream->copy_from_next_packets = 0;
}
break;
}
if (copy_next > stream->curr_pck.data_len) {
remain = copy_next - stream->curr_pck.data_len;
copy_next = stream->curr_pck.data_len;
}
memcpy(packet+pos, stream->curr_pck.data + stream->pck_offset, copy_next);
stream->pck_offset += copy_next;
assert(stream->pes_data_remain >= copy_next);
stream->pes_data_remain -= copy_next;
if (stream->copy_from_next_packets > copy_next) {
stream->copy_from_next_packets -= copy_next;
} else {
stream->copy_from_next_packets = 0;
}
if (stream->pck_offset == stream->curr_pck.data_len) {
assert(!remain || (remain>=stream->min_bytes_copy_from_next));
if (stream->discard_data) gf_free(stream->curr_pck.data);
stream->curr_pck.data = NULL;
stream->curr_pck.data_len = 0;
stream->pck_offset = 0;
}
if (!remain) break;
pos += copy_next;
copy_next = remain;
}
}
else if (stream->program->mux->force_pat_pmt_state==GF_SEG_BOUNDARY_START) {
stream->program->mux->force_pat_pmt_state = GF_SEG_BOUNDARY_FORCE_PAT;
stream->program->mux->force_pat = GF_TRUE;
}
}
stream->bytes_since_last_time += 188;
}
GF_M2TS_Mux_Stream *gf_m2ts_stream_new(u32 pid) {
GF_M2TS_Mux_Stream *stream;
GF_SAFEALLOC(stream, GF_M2TS_Mux_Stream);
if (!stream) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate\n", pid));
return NULL;
}
stream->pid = pid;
stream->process = gf_m2ts_stream_process_pes;
return stream;
}
GF_Err gf_m2ts_output_ctrl(GF_ESInterface *_self, u32 ctrl_type, void *param)
{
GF_ESIPacket *esi_pck;
GF_M2TS_Mux_Stream *stream = (GF_M2TS_Mux_Stream *)_self->output_udta;
switch (ctrl_type) {
case GF_ESI_OUTPUT_DATA_DISPATCH:
esi_pck = (GF_ESIPacket *)param;
if (stream->force_new || (esi_pck->flags & GF_ESI_DATA_AU_START)) {
if (stream->pck_reassembler) {
gf_mx_p(stream->mx);
if (!stream->pck_first) {
stream->pck_first = stream->pck_last = stream->pck_reassembler;
} else {
stream->pck_last->next = stream->pck_reassembler;
stream->pck_last = stream->pck_reassembler;
}
gf_mx_v(stream->mx);
stream->pck_reassembler = NULL;
}
}
if (!stream->pck_reassembler) {
GF_SAFEALLOC(stream->pck_reassembler, GF_M2TS_Packet);
if (!stream->pck_reassembler) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] PID %d: fail to allocate packet reassembler\n", stream->pid));
return GF_OUT_OF_MEM;
}
stream->pck_reassembler->cts = esi_pck->cts;
stream->pck_reassembler->dts = esi_pck->dts;
stream->pck_reassembler->duration = esi_pck->duration;
if (esi_pck->mpeg2_af_descriptors) {
stream->pck_reassembler->mpeg2_af_descriptors = (char*)gf_realloc(stream->pck_reassembler->mpeg2_af_descriptors, sizeof(u8)* (stream->pck_reassembler->mpeg2_af_descriptors_size + esi_pck->mpeg2_af_descriptors_size) );
memcpy(stream->pck_reassembler->mpeg2_af_descriptors + stream->pck_reassembler->mpeg2_af_descriptors_size, esi_pck->mpeg2_af_descriptors, sizeof(u8)* esi_pck->mpeg2_af_descriptors_size );
stream->pck_reassembler->mpeg2_af_descriptors_size += esi_pck->mpeg2_af_descriptors_size;
}
}
stream->force_new = esi_pck->flags & GF_ESI_DATA_AU_END ? GF_TRUE : GF_FALSE;
stream->pck_reassembler->data = (char*)gf_realloc(stream->pck_reassembler->data , sizeof(char)*(stream->pck_reassembler->data_len+esi_pck->data_len) );
memcpy(stream->pck_reassembler->data + stream->pck_reassembler->data_len, esi_pck->data, esi_pck->data_len);
stream->pck_reassembler->data_len += esi_pck->data_len;
stream->pck_reassembler->flags |= esi_pck->flags;
if (stream->force_new) {
gf_mx_p(stream->mx);
if (!stream->pck_first) {
stream->pck_first = stream->pck_last = stream->pck_reassembler;
} else {
stream->pck_last->next = stream->pck_reassembler;
stream->pck_last = stream->pck_reassembler;
}
gf_mx_v(stream->mx);
stream->pck_reassembler = NULL;
}
break;
}
return GF_OK;
}
static void gf_m2ts_stream_set_default_slconfig(GF_M2TS_Mux_Stream *stream)
{
if (!stream->ifce->sl_config) {
stream->ifce->sl_config = (GF_SLConfig *)gf_odf_desc_new(GF_ODF_SLC_TAG);
stream->ifce->sl_config->useAccessUnitStartFlag = 1;
stream->ifce->sl_config->useAccessUnitEndFlag = 1;
stream->ifce->sl_config->useRandomAccessPointFlag = 1;
stream->ifce->sl_config->useTimestampsFlag = 1;
}
}
static s32 gf_m2ts_stream_index(GF_M2TS_Mux_Program *program, u32 pid, u32 stream_id)
{
s32 i=0;
GF_M2TS_Mux_Stream *st = program->streams;
while (st) {
if (pid && (st->pid == pid))
return i;
if (stream_id && (st->ifce->stream_id == stream_id))
return i;
st = st->next;
i++;
}
return -1;
}
static void gf_m2ts_stream_add_hierarchy_descriptor(GF_M2TS_Mux_Stream *stream)
{
GF_M2TSDescriptor *desc;
GF_BitStream *bs;
u32 data_len;
if (!stream || !stream->program || !stream->program->pmt) return;
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 0, 4);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, gf_m2ts_stream_index(stream->program, stream->pid, 0), 6);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, 1, 1);
gf_bs_write_int(bs, gf_m2ts_stream_index(stream->program, 0, stream->ifce->depends_on_stream), 6);
gf_bs_write_int(bs, 3, 2);
gf_bs_write_int(bs, stream->ifce->stream_id, 6);
GF_SAFEALLOC(desc, GF_M2TSDescriptor);
if (!desc) return;
desc->tag = (u8) GF_M2TS_HIERARCHY_DESCRIPTOR;
gf_bs_get_content(bs, &desc->data, &data_len);
gf_bs_del(bs);
desc->data_len = (u8) data_len;
gf_list_add(stream->loop_descriptors, desc);
}
static void gf_m2ts_stream_add_metadata_pointer_descriptor(GF_M2TS_Mux_Program *program)
{
GF_M2TSDescriptor *desc;
GF_BitStream *bs;
u32 data_len;
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_u16(bs, 0xFFFF);
gf_bs_write_u32(bs, GF_4CC('I','D','3',' '));
gf_bs_write_u8(bs, 0xFF);
gf_bs_write_u32(bs, GF_4CC('I','D','3',' '));
gf_bs_write_u8(bs, 0);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_int(bs, 0x1F, 5);
gf_bs_write_u16(bs, program->number);
GF_SAFEALLOC(desc, GF_M2TSDescriptor);
if (!desc) return;
desc->tag = (u8) GF_M2TS_METADATA_POINTER_DESCRIPTOR;
gf_bs_get_content(bs, &desc->data, &data_len);
gf_bs_del(bs);
desc->data_len = (u8) data_len;
gf_list_add(program->loop_descriptors, desc);
}
static void gf_m2ts_stream_add_metadata_descriptor(GF_M2TS_Mux_Stream *stream)
{
GF_M2TSDescriptor *desc;
GF_BitStream *bs;
u32 data_len;
bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
gf_bs_write_u16(bs, 0xFFFF);
gf_bs_write_u32(bs, GF_4CC('I','D','3',' '));
gf_bs_write_u8(bs, 0xFF);
gf_bs_write_u32(bs, GF_4CC('I','D','3',' '));
gf_bs_write_u8(bs, 0);
gf_bs_write_int(bs, 0, 3);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0xF, 4);
GF_SAFEALLOC(desc, GF_M2TSDescriptor);
if (!desc) return;
desc->tag = (u8) GF_M2TS_METADATA_DESCRIPTOR;
gf_bs_get_content(bs, &desc->data, &data_len);
gf_bs_del(bs);
desc->data_len = (u8) data_len;
gf_list_add(stream->loop_descriptors, desc);
}
GF_EXPORT
GF_M2TS_Mux_Stream *gf_m2ts_program_stream_add(GF_M2TS_Mux_Program *program, struct __elementary_stream_ifce *ifce, u32 pid, Bool is_pcr, Bool force_pes)
{
GF_M2TS_Mux_Stream *stream, *st;
stream = gf_m2ts_stream_new(pid);
stream->ifce = ifce;
stream->pid = pid;
stream->program = program;
if (is_pcr) program->pcr = stream;
stream->loop_descriptors = gf_list_new();
if (program->streams) {
if (is_pcr) {
stream->next = program->streams;
program->streams = stream;
} else {
st = program->streams;
while (st->next) st = st->next;
st->next = stream;
}
} else {
program->streams = stream;
}
if (program->pmt) program->pmt->table_needs_update = GF_TRUE;
stream->bit_rate = ifce->bit_rate;
stream->scheduling_priority = 1;
stream->force_single_au = (stream->program->mux->au_pes_mode == GF_M2TS_PACK_ALL) ? GF_FALSE : GF_TRUE;
switch (ifce->stream_type) {
case GF_STREAM_VISUAL:
stream->mpeg2_stream_id = 0xE0;
stream->prevent_two_au_start_in_pes = GF_TRUE;
switch (ifce->object_type_indication) {
case GPAC_OTI_VIDEO_MPEG4_PART2:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG4;
break;
case GPAC_OTI_VIDEO_AVC:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_H264;
stream->min_bytes_copy_from_next = 11;
break;
case GPAC_OTI_VIDEO_SVC:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_SVC;
stream->min_bytes_copy_from_next = 11;
gf_m2ts_stream_add_hierarchy_descriptor(stream);
break;
case GPAC_OTI_VIDEO_HEVC:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_HEVC;
stream->min_bytes_copy_from_next = 12;
break;
case GPAC_OTI_VIDEO_LHVC:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_SHVC;
stream->min_bytes_copy_from_next = 12;
gf_m2ts_stream_add_hierarchy_descriptor(stream);
stream->force_single_au = GF_TRUE;
break;
case GPAC_OTI_VIDEO_MPEG1:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG1;
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:
stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG2;
break;
case GPAC_OTI_IMAGE_JPEG:
case GPAC_OTI_IMAGE_PNG:
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES;
stream->force_single_au = GF_TRUE;
stream->mpeg2_stream_id = 0xFA;
gf_m2ts_stream_set_default_slconfig(stream);
break;
default:
break;
}
break;
case GF_STREAM_AUDIO:
stream->force_single_au = (stream->program->mux->au_pes_mode == GF_M2TS_PACK_NONE) ? GF_TRUE : GF_FALSE;
switch (ifce->object_type_indication) {
case GPAC_OTI_AUDIO_MPEG1:
stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG1;
break;
case GPAC_OTI_AUDIO_MPEG2_PART3:
stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG2;
break;
case GPAC_OTI_AUDIO_AAC_MPEG4:
case GPAC_OTI_AUDIO_AAC_MPEG2_MP:
case GPAC_OTI_AUDIO_AAC_MPEG2_LCP:
case GPAC_OTI_AUDIO_AAC_MPEG2_SSRP:
stream->mpeg2_stream_type = GF_M2TS_AUDIO_LATM_AAC;
stream->mpeg2_stream_type = GF_M2TS_AUDIO_AAC;
if (!ifce->repeat_rate) ifce->repeat_rate = 500;
break;
case GPAC_OTI_AUDIO_AC3:
stream->mpeg2_stream_type = GF_M2TS_AUDIO_AC3;
break;
}
stream->mpeg2_stream_id = 0xC0;
break;
case GF_STREAM_OD:
stream->scheduling_priority = 20;
stream->mpeg2_stream_id = 0xFA;
stream->table_id = GF_M2TS_TABLE_ID_MPEG4_OD;
gf_m2ts_stream_set_default_slconfig(stream);
if (force_pes) {
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES;
stream->force_single_au = GF_TRUE;
} else {
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_SECTIONS;
}
break;
case GF_STREAM_SCENE:
stream->mpeg2_stream_id = 0xFA;
stream->table_id = GF_M2TS_TABLE_ID_MPEG4_BIFS;
gf_m2ts_stream_set_default_slconfig(stream);
if (force_pes) {
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES;
stream->force_single_au = GF_TRUE;
} else {
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_SECTIONS;
}
break;
case GF_STREAM_TEXT:
stream->mpeg2_stream_id = 0xBD;
stream->mpeg2_stream_type = GF_M2TS_METADATA_PES;
gf_m2ts_stream_add_metadata_pointer_descriptor(stream->program);
gf_m2ts_stream_add_metadata_descriptor(stream);
}
if (! (ifce->caps & GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS)) {
if (program->mpeg4_signaling==GF_M2TS_MPEG4_SIGNALING_FULL) {
if (stream->mpeg2_stream_type != GF_M2TS_SYSTEMS_MPEG4_SECTIONS) {
stream->mpeg2_stream_type = GF_M2TS_SYSTEMS_MPEG4_PES;
stream->force_single_au = GF_TRUE;
stream->mpeg2_stream_id = 0xFA;
gf_m2ts_stream_set_default_slconfig(stream);
}
}
}
stream->ifce->output_ctrl = gf_m2ts_output_ctrl;
stream->ifce->output_udta = stream;
stream->mx = gf_mx_new("M2TS PID");
if (ifce->timescale != 90000) {
stream->ts_scale.num = 90000;
stream->ts_scale.den = ifce->timescale;
}
return stream;
}
GF_EXPORT
GF_Err gf_m2ts_program_stream_update_ts_scale(GF_ESInterface *_self, u32 time_scale)
{
GF_M2TS_Mux_Stream *stream = (GF_M2TS_Mux_Stream *)_self->output_udta;
if (!stream || !time_scale)
return GF_BAD_PARAM;
stream->ts_scale.num = 90000;
stream->ts_scale.den = time_scale;
return GF_OK;
}
GF_EXPORT
GF_M2TS_Mux_Program *gf_m2ts_mux_program_find(GF_M2TS_Mux *muxer, u32 program_number)
{
GF_M2TS_Mux_Program *program = muxer->programs;
while (program) {
if (program->number == program_number) return program;
program = program->next;
}
return NULL;
}
GF_EXPORT
GF_M2TS_Mux_Program *gf_m2ts_mux_program_add(GF_M2TS_Mux *muxer, u32 program_number, u32 pmt_pid, u32 pmt_refresh_rate, u32 pcr_offset, Bool mpeg4_signaling)
{
GF_M2TS_Mux_Program *program;
GF_SAFEALLOC(program, GF_M2TS_Mux_Program);
if (!program) return NULL;
program->mux = muxer;
program->mpeg4_signaling = mpeg4_signaling;
program->pcr_offset = pcr_offset;
program->loop_descriptors = gf_list_new();
program->number = program_number;
if (muxer->programs) {
GF_M2TS_Mux_Program *p = muxer->programs;
while (p->next) p = p->next;
p->next = program;
} else {
muxer->programs = program;
}
program->pmt = gf_m2ts_stream_new(pmt_pid);
program->pmt->program = program;
program->pmt->table_needs_update = GF_TRUE;
muxer->pat->table_needs_update = GF_TRUE;
program->pmt->process = gf_m2ts_stream_process_pmt;
program->pmt->refresh_rate_ms = pmt_refresh_rate ? pmt_refresh_rate : (u32) -1;
return program;
}
GF_EXPORT
void gf_m2ts_mux_program_set_name(GF_M2TS_Mux_Program *program, const char *program_name, const char *provider_name)
{
if (program->name) gf_free(program->name);
program->name = program_name ? gf_strdup(program_name) : NULL;
if (program->provider) gf_free(program->provider);
program->provider = provider_name ? gf_strdup(provider_name) : NULL;
if (program->mux->sdt) program->mux->sdt->table_needs_update = GF_TRUE;
}
GF_EXPORT
GF_M2TS_Mux *gf_m2ts_mux_new(u32 mux_rate, u32 pat_refresh_rate, Bool real_time)
{
GF_BitStream *bs;
GF_M2TS_Mux *muxer;
GF_SAFEALLOC(muxer, GF_M2TS_Mux);
if (!muxer) return NULL;
muxer->pat = gf_m2ts_stream_new(GF_M2TS_PID_PAT);
if (!muxer->pat) {
gf_free(muxer);
return NULL;
}
muxer->pat->process = gf_m2ts_stream_process_pat;
muxer->pat->refresh_rate_ms = pat_refresh_rate ? pat_refresh_rate : (u32) -1;
muxer->real_time = real_time;
muxer->bit_rate = mux_rate;
muxer->init_pcr_value = 0;
if (mux_rate) muxer->fixed_rate = GF_TRUE;
bs = gf_bs_new(muxer->null_pck, 188, GF_BITSTREAM_WRITE);
gf_bs_write_int(bs, 0x47, 8);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0, 1);
gf_bs_write_int(bs, 0x1FFF, 13);
gf_bs_write_int(bs, 0, 2);
gf_bs_write_int(bs, 1, 2);
gf_bs_write_int(bs, 0, 4);
gf_bs_del(bs);
gf_rand_init(GF_FALSE);
muxer->pcr_update_ms = 100;
return muxer;
}
GF_EXPORT
void gf_m2ts_mux_enable_sdt(GF_M2TS_Mux *mux, u32 refresh_rate_ms)
{
if (!mux->sdt) {
mux->sdt = gf_m2ts_stream_new(GF_M2TS_PID_SDT_BAT_ST);
mux->sdt->process = gf_m2ts_stream_process_sdt;
mux->sdt->refresh_rate_ms = refresh_rate_ms;
}
mux->sdt->table_needs_update = GF_TRUE;
return;
}
GF_EXPORT
void gf_m2ts_mux_set_pcr_max_interval(GF_M2TS_Mux *muxer, u32 pcr_update_ms)
{
if (muxer && (pcr_update_ms<=100)) muxer->pcr_update_ms = pcr_update_ms;
}
void gf_m2ts_mux_stream_del(GF_M2TS_Mux_Stream *st)
{
while (st->tables) {
GF_M2TS_Mux_Table *tab = st->tables->next;
while (st->tables->section) {
GF_M2TS_Mux_Section *sec = st->tables->section->next;
gf_free(st->tables->section->data);
gf_free(st->tables->section);
st->tables->section = sec;
}
gf_free(st->tables);
st->tables = tab;
}
while (st->pck_first) {
GF_M2TS_Packet *curr_pck = st->pck_first;
st->pck_first = curr_pck->next;
gf_free(curr_pck->data);
gf_free(curr_pck);
}
if (st->curr_pck.data) gf_free(st->curr_pck.data);
if (st->mx) gf_mx_del(st->mx);
if (st->loop_descriptors) {
while (gf_list_count(st->loop_descriptors) ) {
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor*)gf_list_last(st->loop_descriptors);
gf_list_rem_last(st->loop_descriptors);
if (desc->data) gf_free(desc->data);
gf_free(desc);
}
gf_list_del(st->loop_descriptors);
}
gf_free(st);
}
void gf_m2ts_mux_program_del(GF_M2TS_Mux_Program *prog)
{
while (prog->streams) {
GF_M2TS_Mux_Stream *st = prog->streams->next;
gf_m2ts_mux_stream_del(prog->streams);
prog->streams = st;
}
if (prog->loop_descriptors) {
while (gf_list_count(prog->loop_descriptors) ) {
GF_M2TSDescriptor *desc = (GF_M2TSDescriptor*)gf_list_last(prog->loop_descriptors);
gf_list_rem_last(prog->loop_descriptors);
if (desc->data) gf_free(desc->data);
gf_free(desc);
}
gf_list_del(prog->loop_descriptors);
}
gf_m2ts_mux_stream_del(prog->pmt);
if (prog->name) gf_free(prog->name);
if (prog->provider) gf_free(prog->provider);
gf_free(prog);
}
GF_EXPORT
void gf_m2ts_mux_del(GF_M2TS_Mux *mux)
{
while (mux->programs) {
GF_M2TS_Mux_Program *p = mux->programs->next;
gf_m2ts_mux_program_del(mux->programs);
mux->programs = p;
}
gf_m2ts_mux_stream_del(mux->pat);
if (mux->sdt) gf_m2ts_mux_stream_del(mux->sdt);
gf_free(mux);
}
GF_EXPORT
void gf_m2ts_mux_update_config(GF_M2TS_Mux *mux, Bool reset_time)
{
GF_M2TS_Mux_Program *prog;
gf_m2ts_mux_table_update_bitrate(mux, mux->pat);
if (mux->sdt) {
gf_m2ts_mux_table_update_bitrate(mux, mux->sdt);
}
if (!mux->fixed_rate) {
mux->bit_rate = 0;
mux->bit_rate += mux->pat->bit_rate;
if (mux->sdt) {
mux->bit_rate += mux->sdt->bit_rate;
}
}
prog = mux->programs;
while (prog) {
GF_M2TS_Mux_Stream *stream = prog->streams;
while (stream) {
if (!mux->fixed_rate) {
mux->bit_rate += stream->bit_rate;
}
if (reset_time) stream->time.sec = stream->time.nanosec = 0;
stream = stream->next;
}
gf_m2ts_mux_table_update_bitrate(mux, prog->pmt);
if (!mux->fixed_rate) {
mux->bit_rate += prog->pmt->bit_rate;
}
prog = prog->next;
}
if (reset_time) {
mux->time.sec = mux->time.nanosec = 0;
mux->init_sys_time = 0;
}
}
GF_EXPORT
u32 gf_m2ts_get_sys_clock(GF_M2TS_Mux *muxer)
{
return (u32) (gf_sys_clock_high_res() - muxer->init_sys_time)/1000;
}
GF_EXPORT
u32 gf_m2ts_get_ts_clock(GF_M2TS_Mux *muxer)
{
u32 now, init;
init = muxer->init_ts_time.sec*1000 + muxer->init_ts_time.nanosec/1000000;
now = muxer->time.sec*1000 + muxer->time.nanosec/1000000;
return now-init;
}
GF_EXPORT
GF_Err gf_m2ts_mux_use_single_au_pes_mode(GF_M2TS_Mux *muxer, GF_M2TS_PackMode au_pes_mode)
{
if (!muxer) return GF_BAD_PARAM;
muxer->au_pes_mode = au_pes_mode;
return GF_OK;
}
GF_EXPORT
GF_Err gf_m2ts_mux_set_initial_pcr(GF_M2TS_Mux *muxer, u64 init_pcr_value)
{
if (!muxer) return GF_BAD_PARAM;
muxer->init_pcr_value = 1 + init_pcr_value;
return GF_OK;
}
GF_EXPORT
GF_Err gf_m2ts_mux_enable_pcr_only_packets(GF_M2TS_Mux *muxer, Bool enable_forced_pcr)
{
if (!muxer) return GF_BAD_PARAM;
muxer->enable_forced_pcr = enable_forced_pcr;
return GF_OK;
}
GF_EXPORT
const char *gf_m2ts_mux_process(GF_M2TS_Mux *muxer, u32 *status, u32 *usec_till_next)
{
GF_M2TS_Mux_Program *program;
GF_M2TS_Mux_Stream *stream, *stream_to_process;
GF_M2TS_Time time, max_time;
u32 nb_streams, nb_streams_done;
u64 now_us;
char *ret;
u32 res, highest_priority;
Bool flush_all_pes = GF_FALSE;
Bool check_max_time = GF_FALSE;
nb_streams = nb_streams_done = 0;
*status = GF_M2TS_STATE_IDLE;
now_us = gf_sys_clock_high_res();
if (muxer->real_time) {
if (!muxer->init_sys_time) {
muxer->time.sec = muxer->time.nanosec = 0;
gf_m2ts_time_inc(&muxer->time, (u32) (muxer->init_pcr_value ? muxer->init_pcr_value-1 : 0), 27000000);
muxer->init_sys_time = now_us;
muxer->init_ts_time = muxer->time;
} else {
u64 us_diff = now_us - muxer->init_sys_time;
GF_M2TS_Time now = muxer->init_ts_time;
gf_m2ts_time_inc(&now, us_diff, 1000000);
if (gf_m2ts_time_less(&now, &muxer->time)) {
if (usec_till_next) {
u32 diff = muxer->time.sec - now.sec;
diff *= 1000000;
if (now.nanosec <= muxer->time.nanosec) {
diff += (muxer->time.nanosec - now.nanosec) / 1000;
} else {
assert(diff);
diff -= 1000000;
diff += (1000000000 + muxer->time.nanosec - now.nanosec) / 1000;
}
*usec_till_next = diff;
}
return NULL;
}
}
}
stream_to_process = NULL;
time = muxer->time;
max_time.sec = max_time.nanosec = 0;
if (muxer->needs_reconfig) {
gf_m2ts_mux_update_config(muxer, GF_FALSE);
muxer->needs_reconfig = GF_FALSE;
}
if (muxer->flush_pes_at_rap && muxer->force_pat) {
program = muxer->programs;
while (program) {
stream = program->streams;
while (stream) {
if (stream->pes_data_remain) {
flush_all_pes = GF_TRUE;
break;
}
stream = stream->next;
}
program = program->next;
}
}
if (!flush_all_pes) {
res = muxer->pat->process(muxer, muxer->pat);
if ((res && gf_m2ts_time_less_or_equal(&muxer->pat->time, &time)) || muxer->force_pat) {
time = muxer->pat->time;
stream_to_process = muxer->pat;
if (muxer->force_pat) {
muxer->force_pat = GF_FALSE;
muxer->force_pat_pmt_state = GF_SEG_BOUNDARY_FORCE_PMT;
}
goto send_pck;
}
if (muxer->sdt && !muxer->force_pat_pmt_state) {
res = muxer->sdt->process(muxer, muxer->sdt);
if (res && gf_m2ts_time_less_or_equal(&muxer->sdt->time, &time) ) {
time = muxer->sdt->time;
stream_to_process = muxer->sdt;
goto send_pck;
}
}
program = muxer->programs;
while (program) {
res = program->pmt->process(muxer, program->pmt);
if ((res && gf_m2ts_time_less_or_equal(&program->pmt->time, &time)) || (muxer->force_pat_pmt_state==GF_SEG_BOUNDARY_FORCE_PMT)) {
time = program->pmt->time;
stream_to_process = program->pmt;
if (muxer->force_pat_pmt_state==GF_SEG_BOUNDARY_FORCE_PMT)
muxer->force_pat_pmt_state = GF_SEG_BOUNDARY_FORCE_PCR;
goto send_pck;
}
program = program->next;
}
}
if (!muxer->fixed_rate) {
if (!muxer->real_time) {
time.sec = 0xFFFFFFFF;
} else {
check_max_time = GF_TRUE;
}
}
#define FORCE_PCR_FIRST 0
#if FORCE_PCR_FIRST
program = muxer->programs;
while (program) {
stream = program->pcr;
if (!flush_all_pes || (stream->copy_from_next_packets + stream->pes_data_remain) ) {
res = stream->process(muxer, stream);
if (res && gf_m2ts_time_less_or_equal(&stream->time, &time)) {
time = stream->time;
stream_to_process = stream;
goto send_pck;
}
}
program = program->next;
}
#endif
highest_priority = 0;
program = muxer->programs;
while (program) {
stream = program->streams;
while (stream) {
#if FORCE_PCR_FIRST
if (stream != program->pcr)
#endif
{
if (flush_all_pes && !stream->pes_data_remain) {
nb_streams++;
stream = stream->next;
continue;
}
res = stream->process(muxer, stream);
if (!flush_all_pes && muxer->force_pat)
return gf_m2ts_mux_process(muxer, status, usec_till_next);
if (res) {
if (gf_m2ts_time_less(&stream->time, &time)) {
highest_priority = res;
time = stream->time;
stream_to_process = stream;
#if FORCE_PCR_FIRST
goto send_pck;
#endif
}
else if (gf_m2ts_time_equal(&stream->time, &time)) {
if ((res > highest_priority) || ((res == highest_priority) && !stream->ifce->depends_on_stream)) {
highest_priority = res;
time = stream->time;
stream_to_process = stream;
#if FORCE_PCR_FIRST
goto send_pck;
#endif
}
} else if (check_max_time && gf_m2ts_time_less(&max_time, &stream->time)) {
max_time = stream->time;
}
}
}
nb_streams++;
if ((stream->ifce->caps & GF_ESI_STREAM_IS_OVER) && (!res || stream->refresh_rate_ms) )
nb_streams_done ++;
stream = stream->next;
}
program = program->next;
}
send_pck:
ret = NULL;
if (!stream_to_process) {
if (nb_streams && (nb_streams==nb_streams_done)) {
*status = GF_M2TS_STATE_EOS;
} else {
*status = GF_M2TS_STATE_PADDING;
}
if (muxer->fixed_rate) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Inserting empty packet at %d:%09d\n", time.sec, time.nanosec));
ret = muxer->null_pck;
muxer->tot_pad_sent++;
}
} else {
if (stream_to_process->tables) {
gf_m2ts_mux_table_get_next_packet(stream_to_process, muxer->dst_pck);
} else {
gf_m2ts_mux_pes_get_next_packet(stream_to_process, muxer->dst_pck);
}
ret = muxer->dst_pck;
*status = GF_M2TS_STATE_DATA;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] Sending %s from PID %d at %d:%09d - mux time %d:%09d\n", stream_to_process->tables ? "table" : "PES", stream_to_process->pid, time.sec, time.nanosec, muxer->time.sec, muxer->time.nanosec));
if (nb_streams && (nb_streams==nb_streams_done))
*status = GF_M2TS_STATE_EOS;
}
if (ret) {
muxer->tot_pck_sent++;
if (muxer->fixed_rate ) {
gf_m2ts_time_inc(&muxer->time, 1504, muxer->bit_rate);
}
else if (muxer->real_time) {
u64 us_diff = gf_sys_clock_high_res() - muxer->init_sys_time;
muxer->time = muxer->init_ts_time;
gf_m2ts_time_inc(&muxer->time, us_diff, 1000000);
}
else if (stream_to_process) {
muxer->time = time;
}
muxer->pck_sent_over_br_window++;
if (now_us - muxer->last_br_time_us > 1000000) {
u64 size = 8*188*muxer->pck_sent_over_br_window;
muxer->average_birate_kbps = (u32) (size*1000 / (now_us - muxer->last_br_time_us));
muxer->last_br_time_us = now_us;
muxer->pck_sent_over_br_window=0;
}
} else if (muxer->real_time && !muxer->fixed_rate) {
muxer->time = max_time;
}
return ret;
}
#endif