| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre , Cyril Concolato, Romain Bouqueau | |
| * Copyright (c) Telecom ParisTech 2000-2026 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / MPEG2-TS multiplexer sub-project | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include <gpac/mpegts.h> | |
| #include <gpac/constants.h> | |
| #include <gpac/media_tools.h> | |
| #if !defined(GPAC_DISABLE_MPEG2TS_MUX) | |
| /* 90khz internal delay between two updates for bitrate compute per stream */ | |
| #define BITRATE_UPDATE_WINDOW 90000 | |
| /* length of adaptation_field_length; */ | |
| #define ADAPTATION_LENGTH_LENGTH 1 | |
| /* discontinuty flag, random access flag ... */ | |
| #define ADAPTATION_FLAGS_LENGTH 1 | |
| /* length of adaptation_extension_field_length; */ | |
| #define ADAPTATION_EXTENSION_LENGTH_LENGTH 1 | |
| /* AF des flags and co... */ | |
| #define ADAPTATION_EXTENSION_FLAGS_LENGTH 1 | |
| /* length of encoded pcr */ | |
| #define PCR_LENGTH 6 | |
| #define GF_LANG_UNKNOWN GF_4CC('u','n','d',' ') | |
| /*! DASH segment boundary information - these are internal to the muxer*/ | |
| enum | |
| { | |
| /*! not a segment boundary*/ | |
| GF_SEG_BOUNDARY_NONE=0, | |
| /*! segment start*/ | |
| GF_SEG_BOUNDARY_START, | |
| /*! segment start with forced PMT - triggered by setting \ref __m2ts_mux.force_pat, triggers a forced PCR*/ | |
| GF_SEG_BOUNDARY_FORCE_PMT, | |
| /*! segment start with forced PCR*/ | |
| GF_SEG_BOUNDARY_FORCE_PCR, | |
| }; | |
| 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; | |
| /*couldn't compute bitrate - we need to have more info*/ | |
| 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; | |
| } | |
| /*move to nanosec - 0x3B9ACA00 = 1000*1000*1000 */ | |
| 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; | |
| } | |
| /************************************ | |
| * Section-related functions | |
| ************************************/ | |
| 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) | |
| { | |
| 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; | |
| /* check if there is already a table with that id */ | |
| prev_table = NULL; | |
| table = stream->tables; | |
| while (table) { | |
| if (table->table_id == table_id) { | |
| /* if yes, we need to flush the table and increase the version number */ | |
| 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) { | |
| /* if no, the table is created */ | |
| 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; | |
| table->version_number = stream->initial_version_number; | |
| } | |
| 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; | |
| case GF_M2TS_TABLE_ID_SCTE35_SPLICE_INFO: | |
| maxSectionLength = 1024; | |
| 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) { | |
| GF_BitStream *bs; | |
| 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); | |
| /* first header (not included in section length */ | |
| 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); /* reserved bits are all set */ | |
| gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12); | |
| if (use_syntax_indicator) { | |
| /* second header */ | |
| gf_bs_write_int(bs, table_id_extension, 16); | |
| gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ | |
| gf_bs_write_int(bs, table->version_number, 5); | |
| gf_bs_write_int(bs, 1, 1); /* current_next_indicator = 1: we don't send version in advance */ | |
| 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) { | |
| /* place holder for CRC */ | |
| gf_bs_write_u32(bs, 0); | |
| } | |
| gf_bs_get_content(bs, §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; | |
| /*update PMT*/ | |
| 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) { | |
| /*no clue ... */ | |
| 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) | |
| { | |
| GF_SLHeader hdr; | |
| u32 overhead_size; | |
| u32 offset, sl_size; | |
| u32 section_number, nb_sections; | |
| GF_M2TS_Mux_Table *table, *prev_table; | |
| /*max section length for MPEG-4 BIFS and OD*/ | |
| u32 maxSectionLength = 4096; | |
| GF_M2TS_Mux_Section *section, *prev_sec; | |
| /* check if there is already a table with that id */ | |
| prev_table = NULL; | |
| table = stream->tables; | |
| while (table) { | |
| if (table->table_id == table_id) { | |
| /* if yes, we need to flush the table and increase the version number */ | |
| 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) { | |
| /* if no, the table is created */ | |
| 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); | |
| /*SL-packetized data doesn't fit in one section, we must repacketize*/ | |
| 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); | |
| /*remove start flag*/ | |
| hdr.accessUnitStartFlag = 0; | |
| /*fill each section but beware of last packet*/ | |
| offset += maxSectionLength - overhead_size - sl_size; | |
| nb_sections++; | |
| } | |
| } | |
| prev_sec = NULL; | |
| offset = 0; | |
| hdr = stream->sl_header; | |
| while (offset < table_payload_length) { | |
| GF_BitStream *bs; | |
| u32 remain; | |
| u8 *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); | |
| /* first header (not included in section length */ | |
| 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); /* reserved bits are all set */ | |
| gf_bs_write_int(bs, section->length - SECTION_HEADER_LENGTH, 12); | |
| if (use_syntax_indicator) { | |
| /* second header */ | |
| gf_bs_write_int(bs, table_id_extension, 16); | |
| gf_bs_write_int(bs, 3, 2); /* reserved bits are all set */ | |
| gf_bs_write_int(bs, table->version_number, 5); | |
| gf_bs_write_int(bs, 1, 1); /* current_next_indicator = 1: we don't send version in advance */ | |
| gf_bs_write_int(bs, section_number, 8); | |
| section_number++; | |
| gf_bs_write_int(bs, nb_sections-1, 8); | |
| } | |
| /*write sl header*/ | |
| gf_bs_write_data(bs, slhdr, slhdr_size); | |
| gf_free(slhdr); | |
| /*write sl data*/ | |
| gf_bs_write_data(bs, (char *) table_payload + offset, sl_size); | |
| offset += sl_size; | |
| if (use_syntax_indicator) { | |
| /* place holder for CRC */ | |
| gf_bs_write_u32(bs, 0); | |
| } | |
| gf_bs_get_content(bs, §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)); | |
| /*MPEG-4 tables are input streams for the mux, the bitrate is updated when fetching AUs*/ | |
| } | |
| 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, Bool set_discontinuity) | |
| { | |
| 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, set_discontinuity ? 1 : 0, 1); // discontinuity indicator | |
| gf_bs_write_int(bs, is_rap, 1); // random access indicator | |
| gf_bs_write_int(bs, 0, 1); // es priority indicator | |
| gf_bs_write_int(bs, has_pcr, 1); // PCR_flag | |
| gf_bs_write_int(bs, 0, 1); // OPCR flag | |
| gf_bs_write_int(bs, 0, 1); // splicing point flag | |
| gf_bs_write_int(bs, 0, 1); // transport private data flag | |
| gf_bs_write_int(bs, af_descriptors_size ? 1 : 0, 1); // adaptation field extension flag | |
| 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); // reserved | |
| 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); // ltw_flag | |
| gf_bs_write_int(bs, 0, 1); // piecewise_rate_flag | |
| gf_bs_write_int(bs, 0, 1); // seamless_splice_flag | |
| gf_bs_write_int(bs, 0, 1); // af_descriptor_not_present_flag | |
| gf_bs_write_int(bs, 0xF, 4); // reserved | |
| gf_bs_write_data(bs, af_descriptors, af_descriptors_size); | |
| } | |
| gf_bs_write_byte(bs, 0xFF, padding_length); // stuffing byte | |
| return adaptation_length + ADAPTATION_LENGTH_LENGTH; | |
| } | |
| //#define USE_AF_STUFFING | |
| static void gf_m2ts_mux_table_get_next_packet(GF_M2TS_Mux *mux, 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; | |
| gf_assert(section); | |
| bs = mux->pck_bs; | |
| 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 { | |
| //stuffing using adaptation field - seems not well handled by some equipments ... | |
| #ifdef USE_AF_STUFFING | |
| /* in all the following cases, we write an adaptation field */ | |
| adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; | |
| /* we need at least 2 bytes for adaptation field headers (no pcr) */ | |
| 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 | |
| //stuffing according to annex C.3 | |
| padded_bytes = payload_length - section->length + stream->current_section_offset; | |
| payload_length = section->length - stream->current_section_offset; | |
| #endif | |
| } | |
| gf_assert(payload_length + stream->current_section_offset <= section->length); | |
| //CC field shall not be incremented for if adaptation field only, rewind counter | |
| if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) { | |
| if (!stream->continuity_counter) stream->continuity_counter=15; | |
| else stream->continuity_counter--; | |
| } | |
| gf_bs_reassign_buffer(bs, packet, 188); | |
| gf_bs_write_int(bs, 0x47, 8); // sync | |
| gf_bs_write_int(bs, 0, 1); // error indicator | |
| if (stream->current_section_offset == 0) { | |
| gf_bs_write_int(bs, 1, 1); // payload start indicator | |
| } else { | |
| /* No section concatenation yet!!!*/ | |
| gf_bs_write_int(bs, 0, 1); // payload start indicator | |
| } | |
| gf_bs_write_int(bs, 0, 1); /*priority indicator*/ | |
| gf_bs_write_int(bs, stream->pid, 13); /*pid*/ | |
| gf_bs_write_int(bs, 0, 2); /*scrambling indicator*/ | |
| gf_bs_write_int(bs, adaptation_field_control, 2); /*we do not use adaptation field for sections */ | |
| gf_bs_write_int(bs, stream->continuity_counter, 4); /*continuity counter*/ | |
| 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, GF_FALSE); | |
| #endif | |
| /*pointer field*/ | |
| if (!stream->current_section_offset) { | |
| /* no concatenations of sections in ts packets, so start address is 0 */ | |
| gf_bs_write_u8(bs, 0); | |
| } | |
| 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; | |
| /*carousel table*/ | |
| 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; | |
| /*update ES time*/ | |
| gf_m2ts_time_inc(&stream->time, stream->refresh_rate_ms, 1000); | |
| } | |
| } | |
| if (stream->current_table) stream->current_section = stream->current_table->section; | |
| } | |
| } | |
| /*updates number of bytes sent for bitrate compute (MPEG4 sections)*/ | |
| stream->bytes_since_last_time += 188; | |
| } | |
| static u32 gf_m2ts_stream_process_sdt(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream) | |
| { | |
| if (stream->table_needs_update) { /* generate table payload */ | |
| 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); //reserved future use | |
| prog = muxer->programs; | |
| while (prog) { | |
| u32 len = 0; | |
| gf_bs_write_u16(bs, prog->number); | |
| gf_bs_write_int(bs, 0xFF, 6); //reserved future use | |
| gf_bs_write_int(bs, 0, 1); //EIT_schedule_flag | |
| gf_bs_write_int(bs, 0, 1); //EIT_present_following_flag | |
| gf_bs_write_int(bs, 4, 3); //running status | |
| gf_bs_write_int(bs, 0, 1); //free_CA_mode | |
| 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, &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); | |
| 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; | |
| } | |
| static u32 gf_m2ts_stream_process_pat(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream) | |
| { | |
| if (stream->table_needs_update) { /* generate table payload */ | |
| 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); /*reserved*/ | |
| gf_bs_write_int(bs, prog->pmt->pid, 13); /*reserved*/ | |
| prog = prog->next; | |
| } | |
| gf_bs_get_content(bs, &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); | |
| 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; | |
| } | |
| static void gf_m2ts_program_stream_format_updated(GF_M2TS_Mux_Stream *stream); | |
| static s32 gf_m2ts_find_stream(GF_M2TS_Mux_Program *program, u32 pid, u32 stream_id, GF_M2TS_Mux_Stream **out_stream); | |
| static u32 gf_m2ts_stream_process_pmt(GF_M2TS_Mux *muxer, GF_M2TS_Mux_Stream *stream) | |
| { | |
| if (stream->table_needs_update) { /* generate table payload */ | |
| GF_M2TS_Mux_Stream *es; | |
| u8 *payload; | |
| u32 i; | |
| u32 length, nb_streams=0; | |
| u32 info_length = 0; | |
| GF_BitStream *bs; | |
| if (!stream->program->pcr) | |
| abort(); | |
| es = stream->program->streams; | |
| while (es) { | |
| gf_m2ts_program_stream_format_updated(es); | |
| es = es->next; | |
| } | |
| bs = gf_bs_new(NULL,0,GF_BITSTREAM_WRITE); | |
| gf_bs_write_int(bs, 0x7, 3); // reserved | |
| gf_bs_write_int(bs, stream->program->pcr->pid, 13); | |
| gf_bs_write_int(bs, 0xF, 4); // reserved | |
| 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); // program info length =0 | |
| } else { | |
| u32 len; | |
| GF_ESD *esd; | |
| GF_BitStream *bs_iod; | |
| u8 *iod_data; | |
| u32 iod_data_len; | |
| /*rewrite SL config in IOD streams*/ | |
| 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)) { | |
| /*thay should be the same ...*/ | |
| 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); // program info length | |
| gf_bs_write_int(bs, GF_M2TS_MPEG4_IOD_DESCRIPTOR, 8); | |
| len = iod_data_len + 2; | |
| gf_bs_write_int(bs, len, 8); | |
| /* Scope_of_IOD_label : | |
| 0x10 iod unique a l'int�rieur de programme | |
| 0x11 iod unoque dans le flux ts */ | |
| gf_bs_write_int(bs, 2, 8); | |
| gf_bs_write_int(bs, 2, 8); // IOD_label | |
| gf_bs_write_data(bs, iod_data, iod_data_len); | |
| gf_free(iod_data); | |
| } | |
| /*write all other descriptors*/ | |
| 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) { | |
| u32 force_reg = 0; | |
| Bool has_lang = GF_FALSE; | |
| u32 es_info_length = 0; | |
| u8 type = es->mpeg2_stream_type; | |
| nb_streams++; | |
| GF_AVCConfig *avcc = NULL; | |
| GF_HEVCConfig *hvcc = NULL; | |
| GF_AV1Config *av1c = NULL; | |
| switch (es->mpeg2_stream_type) { | |
| case GF_M2TS_AUDIO_AC3: | |
| case GF_M2TS_AUDIO_EC3: | |
| //reg desc | |
| es_info_length += 2 + 4; | |
| //AC3/EC3 desc | |
| es_info_length += 2; | |
| type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| case GF_M2TS_AUDIO_AC4: | |
| //reg desc | |
| es_info_length += 2 + 4; | |
| type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| case GF_M2TS_VIDEO_VC1: | |
| case GF_M2TS_AUDIO_DTS: | |
| case GF_M2TS_AUDIO_OPUS: | |
| //reg desc | |
| es_info_length += 2 + 4; | |
| type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| case GF_M2TS_VIDEO_AV1: | |
| //reg desc + private data specifier desc | |
| es_info_length += 2 + 4 + 2 + 4; | |
| type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| case GF_M2TS_VIDEO_HEVC: | |
| case GF_M2TS_VIDEO_H264: | |
| //check dv | |
| if (es->ifce->caps & GF_ESI_FORCE_DOLBY_VISION) { | |
| type = GF_M2TS_PRIVATE_DATA; | |
| force_reg = GF_M2TS_RA_STREAM_DOVI; | |
| //reg desc | |
| es_info_length += 2 + 4; | |
| } | |
| if (es->ifce->dv_info[0]) { | |
| u32 dv_len = 5; | |
| if (! (es->ifce->dv_info[3] & 0x1)) dv_len+=2; | |
| es_info_length += 2 + dv_len; | |
| } | |
| break; | |
| case GF_M2TS_HLS_AVC_CRYPT: | |
| es_info_length += 6; | |
| break; | |
| case GF_M2TS_HLS_AC3_CRYPT: | |
| es_info_length += 6; | |
| es_info_length += 10; | |
| break; | |
| case GF_M2TS_HLS_EC3_CRYPT: | |
| case GF_M2TS_HLS_AAC_CRYPT: | |
| es_info_length += 6; | |
| es_info_length += 10 + es->ifce->decoder_config_size; | |
| break; | |
| default: | |
| if (es->ifce->codecid==GF_CODECID_DVB_SUBS) { | |
| es_info_length += 2 + 5+3; | |
| } | |
| else if (es->ifce->codecid==GF_CODECID_DVB_TELETEXT) { | |
| es_info_length += 2 + 2+3; | |
| } | |
| else if (es->force_reg_desc) { | |
| es_info_length += 2 + 4; //desc size + reg 4CC | |
| if (!es->ifce->ra_code) { | |
| if (es->ifce->gpac_meta_dsi) { | |
| es_info_length += es->ifce->gpac_meta_dsi_size; | |
| } else { | |
| es_info_length += 4; //codecID | |
| } | |
| } | |
| type = GF_M2TS_PRIVATE_DATA; | |
| } | |
| break; | |
| } | |
| if (es->ifce->decoder_config && (es->mpeg2_stream_type==GF_M2TS_VIDEO_H264)) { | |
| avcc = gf_odf_avc_cfg_read(es->ifce->decoder_config, es->ifce->decoder_config_size); | |
| if (avcc) es_info_length += 6; | |
| } | |
| if (es->ifce->decoder_config && (es->mpeg2_stream_type==GF_M2TS_VIDEO_HEVC) && !es->ifce->depends_on_stream) { | |
| hvcc = gf_odf_hevc_cfg_read(es->ifce->decoder_config, es->ifce->decoder_config_size, GF_FALSE); | |
| if (hvcc) es_info_length += 15; | |
| } | |
| if (es->ifce->decoder_config && (es->mpeg2_stream_type==GF_M2TS_VIDEO_AV1)) { | |
| av1c = gf_odf_av1_cfg_read(es->ifce->decoder_config, es->ifce->decoder_config_size); | |
| if (av1c) es_info_length += 2 + 4; | |
| } | |
| gf_bs_write_int(bs, type, 8); | |
| gf_bs_write_int(bs, 0x7, 3); // reserved | |
| gf_bs_write_int(bs, es->pid, 13); | |
| gf_bs_write_int(bs, 0xF, 4); // reserved | |
| /*calculate es_info_length*/ | |
| if (stream->program->iod && es->ifce && !(es->ifce->caps & GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS)) | |
| es_info_length += 4; | |
| /*another loop descriptors*/ | |
| 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_LANG_UNKNOWN) ) { | |
| es_info_length += 2 + 3; | |
| has_lang = GF_TRUE; | |
| } | |
| gf_bs_write_int(bs, es_info_length, 12); | |
| if (stream->program->iod && es->ifce && !(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); // mpeg4_esid | |
| } | |
| 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); | |
| } | |
| //AVC descriptor | |
| if (avcc) { | |
| gf_bs_write_int(bs, GF_M2TS_AVC_VIDEO_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u8(bs, avcc->AVCProfileIndication); | |
| gf_bs_write_u8(bs, avcc->profile_compatibility); | |
| gf_bs_write_u8(bs, avcc->AVCLevelIndication); | |
| gf_bs_write_u8(bs, 0); | |
| gf_odf_avc_cfg_del(avcc); | |
| } | |
| //HEVC descriptor | |
| if (hvcc) { | |
| gf_bs_write_int(bs, GF_M2TS_HEVC_VIDEO_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 13, 8); | |
| gf_bs_write_int(bs, hvcc->profile_space, 2); | |
| gf_bs_write_int(bs, hvcc->tier_flag, 1); | |
| gf_bs_write_int(bs, hvcc->profile_idc, 5); | |
| gf_bs_write_u32(bs, hvcc->general_profile_compatibility_flags); | |
| gf_bs_write_int(bs, hvcc->progressive_source_flag, 1); | |
| gf_bs_write_int(bs, hvcc->interlaced_source_flag, 1); | |
| gf_bs_write_int(bs, hvcc->non_packed_constraint_flag, 1); | |
| gf_bs_write_int(bs, hvcc->frame_only_constraint_flag, 1); | |
| gf_bs_write_long_int(bs, hvcc->constraint_indicator_flags, 44); | |
| gf_bs_write_int(bs, hvcc->level_idc, 8); | |
| gf_bs_write_int(bs, 0, 1);//temporal subset | |
| gf_bs_write_int(bs, 0, 1);//still present | |
| gf_bs_write_int(bs, 0, 1);//24h present | |
| gf_bs_write_int(bs, 1, 1);//sub_pic_hrd_params_not_present_flag: we don't know, set to 1 | |
| gf_bs_write_int(bs, 0, 2);//reserved | |
| gf_bs_write_int(bs, 2, 2);//HDR_WCG_idc | |
| gf_odf_hevc_cfg_del(hvcc); | |
| } | |
| switch (es->mpeg2_stream_type) { | |
| case GF_M2TS_AUDIO_AC3: | |
| gf_bs_write_int(bs, GF_M2TS_DVB_AC3_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 0, 8); //todo | |
| //also write reg desc | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_AC3); | |
| 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_u32(bs, GF_M2TS_RA_STREAM_VC1); | |
| break; | |
| case GF_M2TS_AUDIO_EC3: | |
| gf_bs_write_int(bs, GF_M2TS_DVB_EAC3_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 0, 8); //todo | |
| //also write reg desc | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_EAC3); | |
| break; | |
| case GF_M2TS_AUDIO_AC4: | |
| //write reg desc | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_AC4); | |
| break; | |
| case GF_M2TS_AUDIO_DTS: | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_DTS3); | |
| break; | |
| case GF_M2TS_AUDIO_OPUS: | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_OPUS); | |
| break; | |
| case GF_M2TS_VIDEO_AV1: | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_AV1); | |
| gf_bs_write_int(bs, GF_M2TS_DVB_PRIVATE_DATA_SPECIFIER_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, GF_4CC('A', 'O', 'M', 'S')); | |
| break; | |
| case GF_M2TS_HLS_AC3_CRYPT: | |
| gf_bs_write_u8(bs, GF_M2TS_PRIVATE_DATA_INDICATOR_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 4); | |
| gf_bs_write_u32(bs, GF_4CC('a', 'c', '3', 'd')); | |
| //add reg descriptor for SAES | |
| gf_bs_write_u8(bs, GF_M2TS_REGISTRATION_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 8); | |
| gf_bs_write_u32(bs, GF_4CC('z', 'a', 'c', '3')); | |
| gf_bs_write_u16(bs, 0); //priming | |
| gf_bs_write_u8(bs, 1); //version | |
| gf_bs_write_u8(bs, 0); | |
| break; | |
| case GF_M2TS_HLS_EC3_CRYPT: | |
| gf_bs_write_u8(bs, GF_M2TS_PRIVATE_DATA_INDICATOR_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 4); | |
| gf_bs_write_u32(bs, GF_4CC('e', 'c', '3', 'd')); | |
| //add reg descriptor for SAES | |
| gf_bs_write_u8(bs, GF_M2TS_REGISTRATION_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 8); | |
| gf_bs_write_u32(bs, GF_4CC('z', 'e', 'c', '3')); | |
| gf_bs_write_u16(bs, 0); //priming | |
| gf_bs_write_u8(bs, 1); //version | |
| gf_bs_write_u8(bs, es->ifce->decoder_config_size); //config size | |
| gf_bs_write_data(bs, es->ifce->decoder_config, es->ifce->decoder_config_size); //config size | |
| break; | |
| case GF_M2TS_HLS_AAC_CRYPT: | |
| gf_bs_write_u8(bs, GF_M2TS_PRIVATE_DATA_INDICATOR_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 4); | |
| gf_bs_write_u32(bs, GF_4CC('a', 'a', 'c', 'd')); | |
| //add reg descriptor for SAES | |
| gf_bs_write_u8(bs, GF_M2TS_REGISTRATION_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 8+es->ifce->decoder_config_size); | |
| gf_bs_write_u32(bs, GF_4CC('z', 'a', 'a', 'c')); | |
| gf_bs_write_u16(bs, 0); //priming | |
| gf_bs_write_u8(bs, 1); //version | |
| gf_bs_write_u8(bs, es->ifce->decoder_config_size); //config size | |
| gf_bs_write_data(bs, es->ifce->decoder_config, es->ifce->decoder_config_size); //config size | |
| break; | |
| case GF_M2TS_HLS_AVC_CRYPT: | |
| gf_bs_write_u8(bs, GF_M2TS_PRIVATE_DATA_INDICATOR_DESCRIPTOR); | |
| gf_bs_write_u8(bs, 4); | |
| gf_bs_write_u32(bs, GF_4CC('z', 'a', 'v', 'c')); | |
| break; | |
| default: | |
| if (es->force_reg_desc && es->ifce && es->ifce->codecid) { | |
| gf_bs_write_u8(bs, GF_M2TS_REGISTRATION_DESCRIPTOR); | |
| if (es->ifce->ra_code) { | |
| gf_bs_write_u8(bs, 4); | |
| gf_bs_write_u32(bs, es->ifce->ra_code); | |
| } else if (es->ifce->gpac_meta_dsi_size) { | |
| gf_bs_write_u8(bs, 4 + es->ifce->gpac_meta_dsi_size); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_GPAC); | |
| gf_bs_write_data(bs, es->ifce->gpac_meta_dsi, es->ifce->gpac_meta_dsi_size); | |
| } else { | |
| gf_bs_write_u8(bs, 8); | |
| gf_bs_write_u32(bs, GF_M2TS_RA_STREAM_GPAC); | |
| gf_bs_write_u32(bs, es->ifce->codecid); | |
| } | |
| } | |
| break; | |
| } | |
| if (force_reg) { | |
| //also write reg desc | |
| gf_bs_write_int(bs, GF_M2TS_REGISTRATION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 4, 8); | |
| gf_bs_write_u32(bs, force_reg); | |
| } | |
| if (es->ifce->dv_info[0]) { | |
| u32 len = 5; | |
| if (! (es->ifce->dv_info[3] & 0x1)) len+=2; | |
| gf_bs_write_int(bs, GF_M2TS_DOLBY_VISION_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, len, 8); | |
| gf_bs_write_u8(bs, es->ifce->dv_info[0]); | |
| gf_bs_write_u8(bs, es->ifce->dv_info[1]); | |
| gf_bs_write_u8(bs, es->ifce->dv_info[2]); | |
| gf_bs_write_u8(bs, es->ifce->dv_info[3]); | |
| //base later not present | |
| if (! (es->ifce->dv_info[3] & 0x1)) { | |
| GF_M2TS_Mux_Stream *base_st = NULL; | |
| gf_m2ts_find_stream(es->program, 0, es->ifce->depends_on_stream, &base_st); | |
| gf_bs_write_int(bs, base_st ? base_st->pid : 0, 13); | |
| gf_bs_write_int(bs, 0, 3); | |
| } | |
| //4bits compatid + 4 bits 0 | |
| gf_bs_write_u8(bs, es->ifce->dv_info[4]); | |
| } | |
| if (es->ifce->codecid==GF_CODECID_DVB_SUBS) { | |
| gf_bs_write_int(bs, GF_M2TS_DVB_SUBTITLING_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 8, 8); | |
| gf_bs_write_u8(bs, (es->ifce->lang>>16) & 0xFF); | |
| gf_bs_write_u8(bs, (es->ifce->lang>>6) & 0xFF); | |
| gf_bs_write_u8(bs, (es->ifce->lang) & 0xFF); | |
| if (es->ifce->decoder_config && (es->ifce->decoder_config_size==5)) { | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[4]); | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[0]); | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[1]); | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[2]); | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[3]); | |
| } else { | |
| gf_bs_write_u8(bs, 0x10); //use normal type, we coud use for hard of hearing (0x20) | |
| gf_bs_write_u16(bs, 0); | |
| gf_bs_write_u16(bs, 0); | |
| } | |
| } | |
| else if (es->ifce->codecid==GF_CODECID_DVB_TELETEXT) { | |
| gf_bs_write_int(bs, GF_M2TS_DVB_SUBTITLING_DESCRIPTOR, 8); | |
| gf_bs_write_int(bs, 5, 8); | |
| gf_bs_write_u8(bs, (es->ifce->lang>>16) & 0xFF); | |
| gf_bs_write_u8(bs, (es->ifce->lang>>6) & 0xFF); | |
| gf_bs_write_u8(bs, (es->ifce->lang) & 0xFF); | |
| if (es->ifce->decoder_config && (es->ifce->decoder_config_size==2)) { | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[0]); | |
| gf_bs_write_u8(bs, es->ifce->decoder_config[1]); | |
| } else { | |
| gf_bs_write_int(bs, 0x02, 5); | |
| gf_bs_write_int(bs, 0, 3); | |
| gf_bs_write_int(bs, 0, 8); | |
| } | |
| } | |
| if (av1c) { | |
| gf_bs_write_int(bs, 0x80 /* TODO check value */, 8); | |
| gf_bs_write_int(bs, 4, 8); // specifier tag and AV1 config | |
| gf_bs_write_int(bs, av1c->marker, 1); | |
| gf_bs_write_int(bs, av1c->version, 7); | |
| gf_bs_write_int(bs, av1c->seq_profile, 3); | |
| gf_bs_write_int(bs, av1c->seq_level_idx_0, 5); | |
| gf_bs_write_int(bs, av1c->seq_tier_0, 1); | |
| gf_bs_write_int(bs, av1c->high_bitdepth, 1); | |
| gf_bs_write_int(bs, av1c->twelve_bit, 1); | |
| gf_bs_write_int(bs, av1c->monochrome, 1); | |
| gf_bs_write_int(bs, av1c->chroma_subsampling_x, 1); | |
| gf_bs_write_int(bs, av1c->chroma_subsampling_y, 1); | |
| gf_bs_write_int(bs, av1c->chroma_sample_position, 2); | |
| gf_bs_write_int(bs, 3, 2); // hdr_wcg_idc | |
| gf_bs_write_int(bs, 0, 1); // reserved_zeros | |
| gf_bs_write_int(bs, av1c->initial_presentation_delay_present, 1); | |
| if (av1c->initial_presentation_delay_present) { | |
| gf_bs_write_int(bs, av1c->initial_presentation_delay_minus_one, 4); | |
| } else { | |
| gf_bs_write_int(bs, 0, 4); // reserved_zeros | |
| } | |
| gf_odf_av1_cfg_del(av1c); | |
| } | |
| 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, &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); | |
| 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" (like ISOBMFF CTTSv1 input) - adjusting to CTS\n", stream->pid, *dts, *cts)); | |
| *dts = *cts; | |
| } | |
| /*Rescale our timestamps and express them in PCR*/ | |
| if (stream->ts_scale.den) { | |
| *cts = gf_timestamp_rescale(*cts, stream->ts_scale.den, stream->ts_scale.num); | |
| *dts = gf_timestamp_rescale(*dts, stream->ts_scale.den, stream->ts_scale.num); | |
| 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; | |
| u64 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 = 1; | |
| } | |
| else if ( (*dts < stream->program->initial_ts) && (stream->program->initial_ts_set==1)) { | |
| 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; | |
| } | |
| /*offset our timestamps*/ | |
| *cts += stream->program->pcr_offset; | |
| *dts += stream->program->pcr_offset; | |
| /*PCR offset, in 90000 hz not in 270000000*/ | |
| 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 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; | |
| //If next_pcr_time < stream->time, we need to inject pure pcr data | |
| 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; | |
| /*if too ahead of mux time, don't insert PCR*/ | |
| 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 void id3_write_size(GF_BitStream *bs, u32 len) | |
| { | |
| u32 size; | |
| size = (len>>21) & 0x7F; | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, size, 7); | |
| size = (len>>14) & 0x7F; | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, size, 7); | |
| size = (len>>7) & 0x7F; | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, size, 7); | |
| size = (len) & 0x7F; | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, size, 7); | |
| } | |
| static void m2ts_id3_tag_create(u8 **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); //major | |
| gf_bs_write_u8(bs, 0); //minor | |
| gf_bs_write_u8(bs, 0); //flags | |
| id3_write_size(bs, *len + 10); | |
| gf_bs_write_u32(bs, GF_ID3V2_FRAME_TXXX); | |
| id3_write_size(bs, *len); /* size of the text */ | |
| 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); | |
| } | |
| //get the number of bytes that WILL be added by framing in gf_m2ts_stream_process_pes | |
| static u32 gf_m2ts_stream_get_reframe_overhead(GF_M2TS_Mux_Stream *stream, u32 dts, u32 pay_size) | |
| { | |
| //other types are (currently) either 0 or constant | |
| if (stream->mpeg2_stream_type != GF_M2TS_AUDIO_LATM_AAC) | |
| return stream->reframe_overhead; | |
| //LATM overhead size varries based on | |
| //- the frequency at which we inject the DSI (based on dts) | |
| //- the length of the payload because of non-aligned syntax... | |
| u32 nb_bits=24; //LATM header | |
| //use same condition as in gf_m2ts_stream_process_pes | |
| u32 stream_time_ms = dts/90; | |
| if (stream->ifce->decoder_config && (!stream_time_ms || (stream->latm_last_aac_time + stream->refresh_rate_ms < stream_time_ms-1))) { | |
| nb_bits += 16; | |
| nb_bits += stream->ifce->decoder_config_size*8; | |
| nb_bits += 13; | |
| } else { | |
| nb_bits += 1; | |
| } | |
| while (pay_size>=255) { | |
| nb_bits += 8; | |
| pay_size -= 255; | |
| } | |
| nb_bits += 8; | |
| while (nb_bits % 8) | |
| nb_bits++; | |
| return nb_bits / 8; | |
| } | |
| 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) { | |
| /*section has just been updated */ | |
| if (stream->table_needs_send) | |
| return stream->scheduling_priority; | |
| /*section is not completely sent yet or this is not the first section of the table*/ | |
| 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; | |
| } | |
| /*PES packet not completely sent yet*/ | |
| else if (stream->curr_pck.data_len && stream->pck_offset < stream->curr_pck.data_len) { | |
| //if in pure PCR mode, check if we can fall back to regular mode and start sending the stream | |
| 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; | |
| } | |
| /*PULL mode*/ | |
| if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) { | |
| if (stream->curr_pck.data_len) { | |
| /*discard packet data if we use SL over PES*/ | |
| if (stream->discard_data) gf_free(stream->curr_pck.data); | |
| if (stream->curr_pck.mpeg2_af_descriptors) gf_free(stream->curr_pck.mpeg2_af_descriptors); | |
| /*release 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; | |
| /*EOS*/ | |
| if (stream->ifce->caps & GF_ESI_STREAM_IS_OVER) return ret; | |
| 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; | |
| /*flush input pipe*/ | |
| if (stream->ifce->input_ctrl) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL); | |
| if (stream->mx) gf_mx_p(stream->mx); | |
| stream->pck_offset = 0; | |
| stream->curr_pck.data_len = 0; | |
| /*fill curr_pck*/ | |
| curr_pck = stream->pck_first; | |
| if (!stream->program->pcr_init_time_set && (stream != stream->program->pcr)) { | |
| curr_pck = NULL; | |
| } | |
| if (!curr_pck) { | |
| if (stream->mx) 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.sap_type = curr_pck->sap_type; | |
| stream->curr_pck.mpeg2_af_descriptors = curr_pck->mpeg2_af_descriptors; | |
| stream->curr_pck.mpeg2_af_descriptors_size = curr_pck->mpeg2_af_descriptors_size; | |
| /*discard first packet*/ | |
| stream->pck_first = curr_pck->next; | |
| gf_free(curr_pck); | |
| stream->discard_data = GF_TRUE; | |
| if (stream->mx) gf_mx_v(stream->mx); | |
| } | |
| if (!(stream->curr_pck.flags & GF_ESI_DATA_HAS_DTS)) | |
| stream->curr_pck.dts = stream->curr_pck.cts; | |
| /*initializing the PCR*/ | |
| 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(); | |
| } | |
| if (stream->program->pcr_offset==(u64)-1) { | |
| stream->program->pcr_offset = 0; | |
| if (muxer->fixed_rate) { | |
| //estimate frame send time in 90khz | |
| u64 r = (stream->curr_pck.data_len * 8); | |
| r *= 90000; | |
| r /= muxer->bit_rate; | |
| r *= 5; //assume 5 frames of offset | |
| stream->program->pcr_offset = r; | |
| } | |
| } | |
| if (stream->program->force_first_pts==GF_FILTER_NO_TS) | |
| stream->program->force_first_pts = 0; | |
| if (stream->program->force_first_pts) { | |
| u64 first_pts = stream->curr_pck.cts; | |
| u64 first_dts = stream->curr_pck.dts; | |
| if (stream->ts_scale.den) { | |
| first_pts = gf_timestamp_rescale(first_pts, stream->ts_scale.den, stream->ts_scale.num); | |
| first_dts = gf_timestamp_rescale(first_dts, stream->ts_scale.den, stream->ts_scale.num); | |
| } | |
| /*the final PTS is computed by: | |
| force_first_pts = first_pts + stream->program->pcr_offset - stream->program->initial_ts + stream->program->pcr_init_time/300; | |
| force_first_dts = first_dts + stream->program->pcr_offset - stream->program->initial_ts + stream->program->pcr_init_time/300; | |
| time_inc is computed as | |
| time_inc = force_first_dts - stream->program->pcr_init_time/300; | |
| => time_inc = first_dts + stream->program->pcr_offset - stream->program->initial_ts + stream->program->pcr_init_time/300 - stream->program->pcr_init_time/300; | |
| => time_inc = first_dts + stream->program->pcr_offset - stream->program->initial_ts; | |
| we want at the initial time_inc=0, hence | |
| stream->program->initial_ts = first_dts + stream->program->pcr_offset; | |
| */ | |
| //if forced first PTS is not for this stream, use the forced one | |
| if (first_pts > stream->program->force_first_pts) { | |
| first_pts = first_dts = stream->program->force_first_pts; | |
| } | |
| stream->program->initial_ts = first_dts + stream->program->pcr_offset; | |
| stream->program->pcr_init_time = 300 * (stream->program->force_first_pts - first_pts - stream->program->pcr_offset + stream->program->initial_ts); | |
| stream->program->initial_ts_set = 2; | |
| } | |
| 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_DEBUG, 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 { | |
| /*PES has been sent, discard internal buffer*/ | |
| if (stream->discard_data) gf_free(stream->curr_pck.data); | |
| if (stream->curr_pck.mpeg2_af_descriptors) gf_free(stream->curr_pck.mpeg2_af_descriptors); | |
| stream->curr_pck.data = NULL; | |
| stream->curr_pck.data_len = 0; | |
| stream->curr_pck.mpeg2_af_descriptors = NULL; | |
| stream->curr_pck.mpeg2_af_descriptors_size = 0; | |
| stream->pck_offset = 0; | |
| /*don't send until PCR is initialized*/ | |
| return 0; | |
| } | |
| } | |
| /*SL-encapsultaion*/ | |
| switch (stream->mpeg2_stream_type) { | |
| case GF_M2TS_SYSTEMS_MPEG4_SECTIONS: | |
| /*update SL config*/ | |
| 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 | |
| gf_assert(stream->sl_header.accessUnitLength + stream->curr_pck.data_len < 65536); /*stream->sl_header.accessUnitLength type is u16*/ | |
| stream->sl_header.accessUnitLength += stream->curr_pck.data_len; | |
| #endif | |
| stream->sl_header.randomAccessPointFlag = (stream->curr_pck.sap_type) ? 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); | |
| /*packet data is now copied in sections, discard it if not pull*/ | |
| 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; | |
| gf_free(stream->curr_pck.mpeg2_af_descriptors); | |
| stream->curr_pck.mpeg2_af_descriptors = NULL; | |
| stream->curr_pck.mpeg2_af_descriptors_size = 0; | |
| } | |
| break; | |
| case GF_M2TS_SYSTEMS_MPEG4_PES: | |
| { | |
| char *src_data; | |
| u32 src_data_len; | |
| /*update SL config*/ | |
| 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 | |
| gf_assert(stream->sl_header.accessUnitLength + stream->curr_pck.data_len < 65536); /*stream->sl_header.accessUnitLength type is u16*/ | |
| stream->sl_header.accessUnitLength += stream->curr_pck.data_len; | |
| #endif | |
| stream->sl_header.randomAccessPointFlag = (stream->curr_pck.sap_type) ? 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); | |
| /*discard src data*/ | |
| 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)); | |
| /*moving from PES to SL reallocates a new buffer, force discard even in pull mode*/ | |
| stream->discard_data = GF_TRUE; | |
| } | |
| break; | |
| /*perform LATM encapsulation*/ | |
| case GF_M2TS_AUDIO_LATM_AAC: | |
| { | |
| u32 size, stream_time_ms; | |
| 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); | |
| //directly use dts - using stream->time will not work when estimating the overhead as this time may change | |
| //between the overhead estimation call and the actual packet reframing | |
| stream_time_ms = stream->curr_pck.dts/90; | |
| if (stream->ifce->decoder_config && (!stream_time_ms || (stream->latm_last_aac_time + stream->refresh_rate_ms < stream_time_ms-1))) { | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_M4ADecSpecInfo cfg; | |
| #endif | |
| stream->latm_last_aac_time = stream_time_ms+1; | |
| gf_bs_write_int(bs, 0, 1); | |
| /*mux config */ | |
| gf_bs_write_int(bs, 0, 1);/*audio mux version = 0*/ | |
| gf_bs_write_int(bs, 1, 1);/*allStreamsSameTimeFraming*/ | |
| gf_bs_write_int(bs, 0, 6);/*numSubFrames*/ | |
| gf_bs_write_int(bs, 0, 4);/*numProgram*/ | |
| gf_bs_write_int(bs, 0, 3);/*numLayer prog 1*/ | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| gf_m4a_get_config(stream->ifce->decoder_config, stream->ifce->decoder_config_size, &cfg); | |
| gf_m4a_write_config_bs(bs, &cfg); | |
| #else | |
| gf_bs_write_data(bs, stream->ifce->decoder_config, stream->ifce->decoder_config_size); | |
| #endif | |
| gf_bs_write_int(bs, 0, 3);/*frameLengthType*/ | |
| gf_bs_write_int(bs, 0, 8);/*latmBufferFullness*/ | |
| gf_bs_write_int(bs, 0, 1);/*other data present*/ | |
| gf_bs_write_int(bs, 0, 1);/*crcCheckPresent*/ | |
| } else { | |
| gf_bs_write_int(bs, 1, 1); | |
| } | |
| /*write payloadLengthInfo*/ | |
| 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; | |
| /*rewrite LATM frame header*/ | |
| size = stream->curr_pck.data_len - 3; | |
| stream->curr_pck.data[1] |= (size>>8) & 0x1F; | |
| stream->curr_pck.data[2] = (size) & 0xFF; | |
| /*since we reallocated the packet data buffer, force a discard in pull mode*/ | |
| stream->discard_data = GF_TRUE; | |
| } | |
| break; | |
| /*perform ADTS encapsulation*/ | |
| case GF_M2TS_AUDIO_AAC: | |
| case GF_M2TS_HLS_AAC_CRYPT: | |
| if (stream->ifce->decoder_config) { | |
| GF_BitStream *bs; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| GF_M4ADecSpecInfo cfg; | |
| 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; | |
| #endif | |
| bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| gf_bs_write_int(bs, 0xFFF, 12);/*sync*/ | |
| gf_bs_write_int(bs, 0, 1);/*mpeg2 aac*/ | |
| gf_bs_write_int(bs, 0, 2); /*layer*/ | |
| gf_bs_write_int(bs, 1, 1); /* protection_absent*/ | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| 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); | |
| if (cfg.program_config_element_present) | |
| gf_bs_write_int(bs, 0, 3); | |
| else | |
| gf_bs_write_int(bs, cfg.chan_cfg, 3); | |
| #else | |
| gf_bs_write_int(bs, GF_M4A_AAC_LC-1, 2); | |
| gf_bs_write_int(bs, 1, 4); //FIXME | |
| gf_bs_write_int(bs, 0, 1); | |
| gf_bs_write_int(bs, 2, 3);//FIXME | |
| #endif | |
| 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); | |
| /*base reframe overhead*/ | |
| stream->reframe_overhead = 7; | |
| #ifndef GPAC_DISABLE_AV_PARSERS | |
| if (cfg.program_config_element_present) { | |
| gf_bs_write_int(bs, 2, 2); | |
| u32 cpe_size = (u32) gf_bs_get_position(bs); | |
| gf_m4a_write_program_config_element_bs(bs, &cfg); | |
| stream->reframe_overhead += (u32) gf_bs_get_position(bs) - cpe_size; | |
| } else | |
| #endif | |
| 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); | |
| } | |
| /*since we reallocated the packet data buffer, force a discard in pull mode*/ | |
| stream->discard_data = GF_TRUE; | |
| break; | |
| case GF_M2TS_METADATA_ID3_HLS: | |
| m2ts_id3_tag_create(&stream->curr_pck.data, &stream->curr_pck.data_len); | |
| stream->discard_data = GF_TRUE; | |
| break; | |
| case GF_M2TS_METADATA_PES: | |
| case GF_M2TS_METADATA_ID3_KLVA: | |
| case GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS: | |
| // nothing to do | |
| if ((stream->ifce->stream_type==GF_STREAM_TEXT) && (stream->ifce->ra_code==GF_M2TS_RA_STREAM_SRT)) { | |
| if (!stream->curr_pck.data_len || !gf_utf8_is_legal(stream->curr_pck.data, stream->curr_pck.data_len)) | |
| return stream->scheduling_priority; | |
| char szHeader[100], szDur[100]; | |
| stream->num_frame++; | |
| sprintf(szHeader, "%u\n00:00:00,000 --> ", stream->num_frame); | |
| char *tx3g_format_time(u64 ts, u32 timescale, char *szDur, Bool is_srt); | |
| tx3g_format_time(stream->curr_pck.duration, stream->ifce->timescale, szDur, GF_TRUE); | |
| gf_strcat(szHeader, szDur); | |
| gf_strcat(szHeader, "\n"); | |
| u32 hlen = (u32) strlen(szHeader); | |
| u8 *data = gf_malloc(stream->curr_pck.data_len+hlen+1); | |
| memcpy(data, szHeader, hlen); | |
| if (stream->curr_pck.data_len) | |
| memcpy(data+hlen, stream->curr_pck.data, stream->curr_pck.data_len); | |
| data[stream->curr_pck.data_len + hlen] = 0; | |
| stream->curr_pck.data_len = stream->curr_pck.data_len+hlen+1; | |
| gf_free(stream->curr_pck.data); | |
| stream->curr_pck.data = data; | |
| stream->discard_data = GF_TRUE; | |
| } | |
| break; | |
| default: | |
| if (stream->ifce->codecid==GF_CODECID_DVB_SUBS) { | |
| GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE); | |
| gf_bs_write_u8(bs, 0x20); | |
| gf_bs_write_u8(bs, 0); | |
| gf_bs_write_data(bs, stream->curr_pck.data, stream->curr_pck.data_len); | |
| 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->discard_data = GF_TRUE; | |
| } | |
| break; | |
| } | |
| if (stream->start_pes_at_rap && (stream->curr_pck.sap_type) | |
| ) { | |
| stream->program->mux->force_pat = GF_TRUE; | |
| } | |
| /*rewrite timestamps for PES header*/ | |
| gf_m2ts_remap_timestamps_for_pes(stream, stream->curr_pck.flags, &stream->curr_pck.dts, &stream->curr_pck.cts, &stream->curr_pck.duration); | |
| /*compute next interesting time in TS unit: this will be DTS of next packet*/ | |
| stream->time = stream->program->ts_time_at_pcr_init; | |
| //safety check | |
| if (stream->curr_pck.dts > stream->program->pcr_init_time/300) | |
| time_inc = stream->curr_pck.dts - stream->program->pcr_init_time/300; | |
| else | |
| time_inc = 0; | |
| /* CBR, estimate number of packets required to send => time in mux rate, and offset send time*/ | |
| if (muxer->fixed_rate) { | |
| u64 clock_diff = (stream->curr_pck.data_len * 188 / 184 + 188) * 8; | |
| clock_diff *= 90000; | |
| clock_diff /= muxer->bit_rate; | |
| if (time_inc > clock_diff) | |
| time_inc -= clock_diff; | |
| else | |
| time_inc = 0; | |
| } | |
| 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)); | |
| /*compute instant bitrate*/ | |
| 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; | |
| /*compute 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; | |
| } | |
| /*in non-realtime mode with no fixed rate we only insert PCR based on DTS */ | |
| 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_next_next_payload_size = 0; | |
| stream->next_pck_flags = 0; | |
| stream->next_pck_sap = 0; | |
| stream->copy_from_next_packets = 0; | |
| //AU packing in single PES is disabled | |
| if (stream->force_single_au) return; | |
| if (stream->ifce->caps & GF_ESI_AU_PULL_CAP) { | |
| GF_ESIPacket test_pck; | |
| test_pck.data_len = 0; | |
| /*pull next data but do not release it since it might be needed later on*/ | |
| 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_sap = test_pck.sap_type; | |
| stream->next_pck_cts = test_pck.cts; | |
| stream->next_pck_dts = test_pck.dts; | |
| } | |
| } else { | |
| /*flush input*/ | |
| 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; | |
| stream->next_pck_sap = stream->pck_first->sap_type; | |
| 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->pck_first->next->next && stream->ifce->input_ctrl) stream->ifce->input_ctrl(stream->ifce, GF_ESI_INPUT_DATA_FLUSH, NULL); | |
| if (stream->pck_first->next->next) { | |
| stream->next_next_next_payload_size = stream->pck_first->next->next->data_len; | |
| } | |
| } | |
| } | |
| } | |
| /*consider we don't have the next AU if: | |
| 1- we are asked to start new PES at RAP, just consider we don't have the next AU*/ | |
| if (stream->start_pes_at_rap && stream->next_pck_sap) { | |
| ignore_next = GF_TRUE; | |
| // stream->program->mux->force_pat_pmt_state = GF_SEG_BOUNDARY_START; | |
| } | |
| /*if we have a RAP about to start on a stream in this program, force all other streams to stop merging data in their current PES*/ | |
| 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; | |
| stream->next_pck_sap = 0; | |
| } | |
| if (stream->next_payload_size) { | |
| stream->next_payload_size += gf_m2ts_stream_get_reframe_overhead(stream, stream->pck_first->dts, stream->next_payload_size); | |
| if (stream->next_next_payload_size) { | |
| stream->next_next_payload_size += gf_m2ts_stream_get_reframe_overhead(stream, stream->pck_first->next->dts, stream->next_next_payload_size); | |
| if (stream->next_next_next_payload_size) { | |
| stream->next_next_next_payload_size += gf_m2ts_stream_get_reframe_overhead(stream, stream->pck_first->next->next->dts, stream->next_next_payload_size); | |
| } | |
| } | |
| 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) | |
| { | |
| gf_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 we have next payload ready, compute transmitted size*/ | |
| if (stream->next_payload_size) { | |
| u32 pck_size = stream->curr_pck.data_len - stream->pck_offset; | |
| u32 ts_bytes = payload_length; | |
| /*finish this AU*/ | |
| while (ts_bytes < pck_size) { | |
| ts_bytes += 184; | |
| } | |
| /*current AU started in PES, don't start new AU - if enough data | |
| still to be sent from current AU, don't send the complete AU | |
| in order to avoid padding*/ | |
| if (stream->prevent_two_au_start_in_pes && !stream->pck_offset) { | |
| if (ts_bytes>184) | |
| ts_bytes -= 184; | |
| else | |
| ts_bytes = pck_size; | |
| } | |
| /*try to fit in part of the next AU*/ | |
| else if (stream->next_payload_size) { | |
| /*how much more TS packets do we need to send next AU ?*/ | |
| while (ts_bytes < pck_size + stream->next_payload_size) { | |
| //check we have enough bytes in the next 3 AUs if we increase by 1 TS packet the PES | |
| //bytes_to_copy from next will be: ts_bytes + 184 - pck_size | |
| //check bytes_to_copy is <= next_payload_size + next_next_payload_size + next_next_next_payload_size | |
| //if not, do not add - we do not want to take the risk of having no data while filling a PES packet with an announced length | |
| if (ts_bytes + 184 > pck_size + stream->next_payload_size + stream->next_next_payload_size + stream->next_next_next_payload_size) | |
| break; | |
| ts_bytes += 184; | |
| } | |
| /*don't end next AU in next PES if we don't want to start 2 AUs in one PES | |
| if we don't have the N+2 AU size, don't try to pack it*/ | |
| 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; | |
| } | |
| } | |
| /*that's how much bytes we copy from the following AUs*/ | |
| if (ts_bytes >= pck_size) { | |
| stream->copy_from_next_packets = ts_bytes - pck_size; | |
| //very low rate case, we didn't enter the previous while loop: | |
| //we can't fill the complete TS packet with the next two AUs, do not copy over | |
| if (stream->copy_from_next_packets > stream->next_payload_size + stream->next_next_payload_size + stream->next_next_next_payload_size) | |
| stream->copy_from_next_packets = 0; | |
| } 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 we don't have enough space in the PES to store beginning of new AU, don't copy it and ask | |
| to recompute header (we might no longer have DTS/CTS signaled)*/ | |
| 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; | |
| stream->next_pck_sap = 0; | |
| return GF_FALSE; | |
| } | |
| /*if what will remain after copying next AU is less than the minimum safety copy only copy next AU and | |
| realign n+2 AU start with PES*/ | |
| 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; | |
| stream->next_pck_sap = 0; | |
| return GF_FALSE; | |
| } | |
| 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; | |
| /*not done with the current pes*/ | |
| if (stream->pes_data_remain) return 0; | |
| hdr_len = 9; | |
| /*signal timing only if AU start in the PES*/ | |
| 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);//packet start code | |
| gf_bs_write_u8(bs, stream->mpeg2_stream_id);// stream id | |
| /*next AU start in current PES and current AU began in previous PES, use next AU timing*/ | |
| 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; | |
| } | |
| /*we already sent the beginning of the AU*/ | |
| 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; | |
| } | |
| /*PES packet length: number of bytes in the PES packet following the last byte of the field "pes packet length"*/ | |
| //we allow 0-length PES packet as some media streams may have a signification for this | |
| pes_len = stream->pes_data_len + 3; // 3 = header size | |
| 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); // pes packet length | |
| gf_bs_write_int(bs, 0x2, 2); // reserved | |
| gf_bs_write_int(bs, 0x0, 2); // scrambling | |
| gf_bs_write_int(bs, 0x0, 1); // priority | |
| gf_bs_write_int(bs, stream->pck_offset ? 0 : 1, 1); // alignment indicator - we could also check start codes to see if we are aligned at slice/video packet level | |
| gf_bs_write_int(bs, 0x0, 1); // copyright | |
| gf_bs_write_int(bs, 0x0, 1); // original or copy | |
| gf_bs_write_int(bs, use_pts, 1); | |
| gf_bs_write_int(bs, use_dts, 1); | |
| gf_bs_write_int(bs, 0x0, 6); //6 flags = 0 (ESCR, ES_rate, DSM_trick, additional_copy, PES_CRC, PES_extension) | |
| 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); // reserved '0011' || '0010' | |
| t = ((cts >> 30) & 0x7); | |
| gf_bs_write_long_int(bs, t, 3); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| t = ((cts >> 15) & 0x7fff); | |
| gf_bs_write_long_int(bs, t, 15); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| t = cts & 0x7fff; | |
| gf_bs_write_long_int(bs, t, 15); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| } | |
| if (use_dts) { | |
| gf_bs_write_int(bs, 0x1, 4); // reserved '0001' | |
| t = ((dts >> 30) & 0x7); | |
| gf_bs_write_long_int(bs, t, 3); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| t = ((dts >> 15) & 0x7fff); | |
| gf_bs_write_long_int(bs, t, 15); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| t = dts & 0x7fff; | |
| gf_bs_write_long_int(bs, t, 15); | |
| gf_bs_write_int(bs, 1, 1); // marker bit | |
| } | |
| 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; // 4 = start code + stream_id | |
| } | |
| 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; | |
| gf_assert(stream->pid); | |
| 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); | |
| /*we may need two pass in case we first compute hdr len and TS payload size by considering | |
| we concatenate next au start in this PES but finally couldn't do it when computing PES len | |
| and AU alignment constraint of the 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 we forced inserting PAT/PMT before new RAP, also insert PCR here*/ | |
| 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) { | |
| //check if PCR, if send at next packet, exceeds requested PCR update time | |
| 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) { | |
| /*AF headers + PCR*/ | |
| payload_length -= 8; | |
| adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; | |
| } | |
| //af descriptors are only inserted at the start of the pes for the time being | |
| if (hdr_len && stream->curr_pck.mpeg2_af_descriptors) { | |
| if (adaptation_field_control == GF_M2TS_ADAPTATION_NONE) { | |
| payload_length -= 2; //AF header but no PCR | |
| adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; | |
| } | |
| payload_length -= 2 + stream->curr_pck.mpeg2_af_descriptors_size; //AF extension field and AF descriptor | |
| } | |
| if (hdr_len) { | |
| gf_assert(!stream->pes_data_remain); | |
| if (! gf_m2ts_stream_compute_pes_length(stream, payload_length)) { | |
| first_pass = GF_FALSE; | |
| continue; | |
| } | |
| gf_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; | |
| /*end of PES packet*/ | |
| if (payload_to_copy >= stream->pes_data_remain) { | |
| payload_to_copy = stream->pes_data_remain; | |
| copy_next = 0; | |
| } | |
| /*packet exceed payload length*/ | |
| if (payload_to_copy >= payload_length) { | |
| padding_length = 0; | |
| payload_to_copy = payload_length; | |
| } | |
| /*packet + next packet exceed payload length*/ | |
| else if (payload_to_copy + copy_next >= payload_length) { | |
| padding_length = 0; | |
| } | |
| /*packet + next packet less than payload length - pad */ | |
| else { | |
| /*AF headers*/ | |
| if (!needs_pcr) { | |
| if (adaptation_field_control == GF_M2TS_ADAPTATION_NONE) { | |
| payload_length -= 2; | |
| adaptation_field_control = GF_M2TS_ADAPTATION_AND_PAYLOAD; | |
| } | |
| } | |
| /*cannot add adaptation field for this TS packet with this payload, we need to split in 2 TS packets*/ | |
| 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 | |
| //CC field shall not be incremented for if adaptation field only, rewind counter | |
| if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) { | |
| if (!stream->continuity_counter) stream->continuity_counter=15; | |
| else stream->continuity_counter--; | |
| } | |
| bs = stream->program->mux->pck_bs; | |
| gf_bs_reassign_buffer(bs, packet, 188); | |
| //small opt for formatting TS header for PES, don't use bitstream for header, slightly faster | |
| #if 0 | |
| gf_bs_write_int(bs, 0x47, 8); // sync byte | |
| gf_bs_write_int(bs, 0, 1); // error indicator | |
| gf_bs_write_int(bs, hdr_len ? 1 : 0, 1); // start ind | |
| gf_bs_write_int(bs, 0, 1); // transport priority | |
| gf_bs_write_int(bs, stream->pid, 13); // pid | |
| gf_bs_write_int(bs, 0, 2); // scrambling | |
| gf_bs_write_int(bs, adaptation_field_control, 2); // we do not use adaptation field for sections | |
| gf_bs_write_int(bs, stream->continuity_counter, 4); // continuity counter | |
| #else | |
| packet[0] = 0x47; // sync byte | |
| packet[1] = (stream->pid>>8) & 0x1F; //high bits of PID | |
| if (hdr_len) packet[1] |= 0x40; // start ind | |
| packet[2] = stream->pid & 0xFF; //low bits of PID | |
| packet[3] = (adaptation_field_control<<4) | (stream->continuity_counter & 0xF); //AF + CC | |
| gf_bs_seek(bs, 4); | |
| #endif | |
| 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; | |
| //allow 2 ms drift | |
| 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); | |
| //since we currently only send the PCR when an AU is sent, it may happen that we exceed PCR the refresh rate depending in the target bitrate and frame rate. | |
| //we only throw a warning when twiice the PCR refresh is exceeded | |
| 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_DEBUG, 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.sap_type) ) ? 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, stream->set_initial_disc); | |
| stream->set_initial_disc = GF_FALSE; | |
| 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; | |
| } | |
| stream->pck_sap_type = 0; | |
| stream->pck_sap_time = 0; | |
| if (hdr_len) { | |
| gf_m2ts_stream_add_pes_header(bs, stream); | |
| if (stream->curr_pck.sap_type) { | |
| stream->pck_sap_type = 1; | |
| stream->pck_sap_time = stream->curr_pck.cts; | |
| } | |
| } | |
| pos = (u32) gf_bs_get_position(bs); | |
| if (adaptation_field_control == GF_M2TS_ADAPTATION_ONLY) { | |
| return; | |
| } | |
| if (stream->curr_pck.data_len - stream->pck_offset >= payload_to_copy) | |
| memcpy(packet+pos, stream->curr_pck.data + stream->pck_offset, payload_to_copy); | |
| else { | |
| gf_assert(0); | |
| } | |
| stream->pck_offset += payload_to_copy; | |
| if (stream->pes_data_remain >= payload_to_copy) | |
| stream->pes_data_remain -= payload_to_copy; | |
| else { | |
| stream->pes_data_remain = 0; | |
| gf_assert(0); | |
| } | |
| /*update stream time, including headers | |
| Note that we don't do that if mux is not operating in fixed rate because the rate can be wrong (way too low) | |
| causing time to increase too fast on IDRs | |
| */ | |
| if (stream->program->mux->fixed_rate) | |
| gf_m2ts_time_inc(&stream->time, 1504/*188*8*/, 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_DEBUG, 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 | |
| )); | |
| } | |
| /*PES has been sent, discard internal buffer*/ | |
| if (stream->discard_data) gf_free(stream->curr_pck.data); | |
| stream->curr_pck.data = NULL; | |
| stream->curr_pck.data_len = 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; | |
| stream->pck_offset = 0; | |
| //#ifndef GPAC_DISABLE_LOG | |
| #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 | |
| /*copy over from next PES to fill this TS packet*/ | |
| if (stream->copy_from_next_packets) { | |
| pos += payload_to_copy; | |
| copy_next = payload_length - payload_to_copy; | |
| /*we might need a more than one*/ | |
| 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) and %u bytes remaining - filling with 0xFF\n", stream->pes_data_remain, 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; | |
| gf_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) { | |
| gf_assert(!remain || (remain>=stream->min_bytes_copy_from_next)); | |
| /*PES has been sent, discard internal buffer*/ | |
| if (stream->discard_data) gf_free(stream->curr_pck.data); | |
| stream->curr_pck.data = NULL; | |
| stream->curr_pck.data_len = 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; | |
| 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 = 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) { | |
| if (stream->mx) 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; | |
| } | |
| if (stream->mx) 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) ); | |
| if (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 (esi_pck->sap_type) stream->pck_reassembler->sap_type = esi_pck->sap_type; | |
| if (stream->force_new) { | |
| if (stream->mx) 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; | |
| } | |
| if (stream->mx) 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_find_stream(GF_M2TS_Mux_Program *program, u32 pid, u32 stream_id, GF_M2TS_Mux_Stream **out_stream) | |
| { | |
| s32 i=0; | |
| GF_M2TS_Mux_Stream *st = program->streams; | |
| if (out_stream) *out_stream = NULL; | |
| while (st) { | |
| if (pid && (st->pid == pid)) { | |
| if (out_stream) *out_stream = st; | |
| return i; | |
| } | |
| if (stream_id && (st->ifce->stream_id == stream_id)) { | |
| if (out_stream) *out_stream = st; | |
| 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); | |
| /*reserved*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*temporal_scalability_flag: use the reserved value*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*spatial_scalability_flag: use the reserved value*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*quality_scalability_flag: use the reserved value*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*hierarchy_type: use the rerserved value*/ | |
| gf_bs_write_int(bs, 0, 4); | |
| /*reserved*/ | |
| gf_bs_write_int(bs, 3, 2); | |
| /*hierarchy_layer_index*/ | |
| gf_bs_write_int(bs, gf_m2ts_find_stream(stream->program, stream->pid, 0, NULL), 6); | |
| /*tref_present_flag = 1 : NOT PRESENT*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*reserved*/ | |
| gf_bs_write_int(bs, 1, 1); | |
| /*hierarchy_embedded_layer_index*/ | |
| gf_bs_write_int(bs, gf_m2ts_find_stream(stream->program, 0, stream->ifce->depends_on_stream, NULL), 6); | |
| /*reserved*/ | |
| gf_bs_write_int(bs, 3, 2); | |
| /*hierarchy_channel*/ | |
| 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_M2TS_META_ID3); | |
| gf_bs_write_u8(bs, 0xFF); | |
| gf_bs_write_u32(bs, GF_M2TS_META_ID3); | |
| gf_bs_write_u8(bs, 0); /* service id */ | |
| gf_bs_write_int(bs, 0, 1); /* locator */ | |
| gf_bs_write_int(bs, 0, 2); /* carriage flags */ | |
| gf_bs_write_int(bs, 0x1F, 5); /* reserved */ | |
| 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_M2TS_META_ID3); | |
| gf_bs_write_u8(bs, 0xFF); | |
| gf_bs_write_u32(bs, GF_M2TS_META_ID3); | |
| gf_bs_write_u8(bs, 0); /* service id */ | |
| gf_bs_write_int(bs, 0, 3); /* decoder config flags */ | |
| gf_bs_write_int(bs, 0, 1); /* dsmcc flag */ | |
| gf_bs_write_int(bs, 0xF, 4); /* reserved */ | |
| 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 | |
| u32 gf_m2ts_mux_program_get_pmt_pid(GF_M2TS_Mux_Program *prog) | |
| { | |
| return prog->pmt ? prog->pmt->pid : 0; | |
| } | |
| GF_EXPORT | |
| u32 gf_m2ts_mux_program_get_pcr_pid(GF_M2TS_Mux_Program *prog) | |
| { | |
| return prog->pcr ? prog->pcr->pid : 0; | |
| } | |
| GF_EXPORT | |
| u32 gf_m2ts_mux_program_get_stream_count(GF_M2TS_Mux_Program *prog) | |
| { | |
| GF_M2TS_Mux_Stream *stream = prog->streams; | |
| u32 count=0; | |
| while (stream) { | |
| count++; | |
| stream = stream->next; | |
| } | |
| return count; | |
| } | |
| static void gf_m2ts_program_stream_format_updated(GF_M2TS_Mux_Stream *stream) | |
| { | |
| struct __elementary_stream_ifce *ifce = stream->ifce; | |
| Bool force_pes = stream->force_pes ? GF_TRUE : GF_FALSE; | |
| switch (ifce->stream_type) { | |
| case GF_STREAM_VISUAL: | |
| /*just pick first valid stream_id in visual range*/ | |
| stream->mpeg2_stream_id = 0xE0; | |
| /*for video streams, prevent sending two frames start in one PES. This will | |
| introduce more overhead at very low bitrates where such cases happen, but will ensure proper timing | |
| of each frame*/ | |
| stream->prevent_two_au_start_in_pes = GF_TRUE; | |
| switch (ifce->codecid) { | |
| case GF_CODECID_MPEG4_PART2: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG4; | |
| break; | |
| case GF_CODECID_AVC: | |
| if (ifce->caps & GF_ESI_STREAM_HLS_SAES) | |
| stream->mpeg2_stream_type = GF_M2TS_HLS_AVC_CRYPT; | |
| else | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_H264; | |
| /*make sure we send AU delim NALU in same PES as first VCL NAL: 6 bytes (start code + 1 nal hdr + AU delim) | |
| + 4 byte start code + first nal header*/ | |
| stream->min_bytes_copy_from_next = 11; | |
| break; | |
| case GF_CODECID_SVC: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_SVC; | |
| /*make sure we send AU delim NALU in same PES as first VCL NAL: 6 bytes (start code + 1 nal hdr + AU delim) | |
| + 4 byte start code + first nal header*/ | |
| stream->min_bytes_copy_from_next = 11; | |
| gf_m2ts_stream_add_hierarchy_descriptor(stream); | |
| break; | |
| case GF_CODECID_HEVC: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_HEVC; | |
| //this is a bit crude and will need refinement | |
| if (ifce->depends_on_stream) { | |
| GF_M2TS_Mux_Stream *base_st=NULL; | |
| if (! (stream->ifce->caps & GF_ESI_FORCE_DOLBY_VISION)) { | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_HEVC_TEMPORAL; | |
| gf_m2ts_stream_add_hierarchy_descriptor(stream); | |
| } | |
| stream->force_single_au = GF_TRUE; | |
| gf_m2ts_find_stream(stream->program, 0, ifce->depends_on_stream, &base_st); | |
| if (base_st) base_st->force_single_au = GF_TRUE; | |
| } | |
| /*make sure we send AU delim NALU in same PES as first VCL NAL: 7 bytes (4 start code + 2 nal header + 1 AU delim) | |
| + 4 byte start code + first nal header*/ | |
| stream->min_bytes_copy_from_next = 12; | |
| break; | |
| case GF_CODECID_LHVC: | |
| //FIXME - we need to check scalability type to see if we have MHVC, for now only use SHVC | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_SHVC; | |
| /*make sure we send AU delim NALU in same PES as first VCL NAL: 7 bytes (4 start code + 2 nal header + 1 AU delim) | |
| + 4 byte start code + first nal header*/ | |
| stream->min_bytes_copy_from_next = 12; | |
| gf_m2ts_stream_add_hierarchy_descriptor(stream); | |
| //force by default with SHVC since we don't have any delimiter / layer yet | |
| stream->force_single_au = GF_TRUE; | |
| break; | |
| case GF_CODECID_MPEG1: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG1; | |
| break; | |
| case GF_CODECID_MPEG2_SIMPLE: | |
| case GF_CODECID_MPEG2_MAIN: | |
| case GF_CODECID_MPEG2_SNR: | |
| case GF_CODECID_MPEG2_SPATIAL: | |
| case GF_CODECID_MPEG2_HIGH: | |
| case GF_CODECID_MPEG2_422: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_MPEG2; | |
| break; | |
| /*JPEG/PNG carried in MPEG-4 PES*/ | |
| case GF_CODECID_JPEG: | |
| case GF_CODECID_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; | |
| case GF_CODECID_VVC: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_VVC; | |
| /*make sure we send AU delim NALU in same PES as first VCL NAL: 7 bytes (4 start code + 2 nal header + 1 AU delim) | |
| + 4 byte start code + first nal header*/ | |
| stream->min_bytes_copy_from_next = 12; | |
| break; | |
| case GF_CODECID_SMPTE_VC1: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_VC1; | |
| break; | |
| case GF_CODECID_AV1: | |
| stream->mpeg2_stream_type = GF_M2TS_VIDEO_AV1; | |
| stream->force_single_au = GF_TRUE; | |
| break; | |
| default: | |
| if (!ifce->ra_code && !stream->force_reg_desc) { | |
| if (ifce->gpac_meta_dsi) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Unsupported mpeg2-ts audio type for codec %s, signaling as PES private using GPAC 4CC in registration descriptor\n", ifce->gpac_meta_name ? ifce->gpac_meta_name : gf_codecid_name(ifce->codecid) )); | |
| } else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Unsupported mpeg2-ts video type for codec %s, signaling as PES private using codec 4CC %s in registration descriptor\n", gf_codecid_name(ifce->codecid), gf_4cc_to_str(ifce->codecid) )); | |
| } | |
| } | |
| stream->mpeg2_stream_type = GF_M2TS_PRIVATE_DATA; | |
| stream->force_single_au = GF_TRUE; | |
| stream->force_reg_desc = GF_TRUE; | |
| break; | |
| } | |
| break; | |
| case GF_STREAM_AUDIO: | |
| /*just pick first valid stream_id in audio range*/ | |
| stream->mpeg2_stream_id = 0xC0; | |
| //override default packing for audio | |
| stream->force_single_au = (stream->program->mux->au_pes_mode == GF_M2TS_PACK_NONE) ? GF_TRUE : GF_FALSE; | |
| switch (ifce->codecid) { | |
| case GF_CODECID_MPEG_AUDIO: | |
| case GF_CODECID_MPEG_AUDIO_L1: | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG1; | |
| break; | |
| case GF_CODECID_MPEG2_PART3: | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_MPEG2; | |
| break; | |
| case GF_CODECID_AAC_MPEG4: | |
| case GF_CODECID_AAC_MPEG2_MP: | |
| case GF_CODECID_AAC_MPEG2_LCP: | |
| case GF_CODECID_AAC_MPEG2_SSRP: | |
| if (ifce->caps & GF_ESI_AAC_USE_LATM) | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_LATM_AAC; | |
| else if (ifce->caps & GF_ESI_STREAM_HLS_SAES) | |
| stream->mpeg2_stream_type = GF_M2TS_HLS_AAC_CRYPT; | |
| else | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_AAC; | |
| stream->refresh_rate_ms = ifce->repeat_rate ? ifce->repeat_rate : 500; | |
| break; | |
| case GF_CODECID_USAC: | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_LATM_AAC; | |
| stream->refresh_rate_ms = ifce->repeat_rate ? ifce->repeat_rate : 500; | |
| //stream->force_single_au = GF_TRUE; | |
| break; | |
| case GF_CODECID_AC3: | |
| if (ifce->caps & GF_ESI_STREAM_HLS_SAES) | |
| stream->mpeg2_stream_type = GF_M2TS_HLS_AC3_CRYPT; | |
| else | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_AC3; | |
| break; | |
| case GF_CODECID_EAC3: | |
| if (ifce->caps & GF_ESI_STREAM_HLS_SAES) | |
| stream->mpeg2_stream_type = GF_M2TS_HLS_EC3_CRYPT; | |
| else | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_EC3; | |
| break; | |
| case GF_CODECID_AC4: | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_AC4; | |
| break; | |
| case GF_CODECID_DTS_CA: | |
| case GF_CODECID_DTS_HD_HR_MASTER: | |
| case GF_CODECID_DTS_HD_LOSSLESS: | |
| case GF_CODECID_DTS_EXPRESS_LBR: | |
| case GF_CODECID_DTS_X: | |
| case GF_CODECID_DTS_Y: | |
| stream->mpeg2_stream_type = GF_M2TS_AUDIO_DTS; | |
| break; | |
| default: | |
| if (!ifce->ra_code && !stream->force_reg_desc) { | |
| if (ifce->gpac_meta_dsi) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Unsupported mpeg2-ts audio type for codec %s, signaling as PES private using GPAC 4CC in registration descriptor\n", ifce->gpac_meta_name ? ifce->gpac_meta_name : gf_codecid_name(ifce->codecid) )); | |
| } else { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Unsupported mpeg2-ts audio type for codec %s, signaling as PES private using codec 4CC in registration descriptor\n", gf_codecid_name(ifce->codecid) )); | |
| } | |
| } | |
| stream->mpeg2_stream_type = GF_M2TS_PRIVATE_DATA; | |
| stream->force_single_au = GF_TRUE; | |
| stream->force_reg_desc = GF_TRUE; | |
| break; | |
| } | |
| break; | |
| case GF_STREAM_OD: | |
| /*highest priority for OD streams as they are needed to process other streams*/ | |
| 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->force_single_au = GF_TRUE; | |
| switch (ifce->codecid) { | |
| case GF_CODECID_DVB_SUBS: | |
| stream->mpeg2_stream_type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| case GF_CODECID_DVB_TELETEXT: | |
| stream->mpeg2_stream_type = GF_M2TS_PRIVATE_DATA; | |
| break; | |
| default: | |
| stream->mpeg2_stream_type = GF_M2TS_METADATA_PES; | |
| if (stream->ifce->ra_code) { | |
| stream->force_reg_desc = GF_TRUE; | |
| } else { | |
| gf_m2ts_stream_add_metadata_pointer_descriptor(stream->program); | |
| gf_m2ts_stream_add_metadata_descriptor(stream); | |
| } | |
| break; | |
| } | |
| break; | |
| case GF_STREAM_METADATA: | |
| if (ifce->codecid == GF_CODECID_SCTE35) { | |
| stream->mpeg2_stream_type = GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS; | |
| break; | |
| } | |
| //fallthrough | |
| default: | |
| GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS Muxer] Unsupported codec %s, signaling as raw data\n", gf_codecid_name(ifce->codecid) )); | |
| stream->mpeg2_stream_id = 0xBD; | |
| stream->mpeg2_stream_type = GF_M2TS_METADATA_PES; | |
| break; | |
| } | |
| } | |
| 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, Bool needs_mutex) | |
| { | |
| 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(); | |
| stream->set_initial_disc = program->initial_disc_set; | |
| if (program->streams) { | |
| /*if PCR keep stream at the beginning*/ | |
| 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_pes = force_pes ? 1 : 0; | |
| stream->force_single_au = (stream->program->mux->au_pes_mode == GF_M2TS_PACK_ALL) ? GF_FALSE : GF_TRUE; | |
| gf_m2ts_program_stream_format_updated(stream); | |
| if (! (ifce->caps & GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS)) { | |
| /*override signaling for all streams except BIFS/OD, to use MPEG-4 PES*/ | |
| 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;/*ISO/IEC14496-1_SL-packetized_stream*/ | |
| gf_m2ts_stream_set_default_slconfig(stream); | |
| } | |
| } | |
| } | |
| stream->ifce->output_ctrl = gf_m2ts_output_ctrl; | |
| stream->ifce->output_udta = stream; | |
| if (needs_mutex) | |
| stream->mx = gf_mx_new("M2TS PID"); | |
| if (ifce->timescale != 90000) { | |
| stream->ts_scale.num = 90000; | |
| stream->ts_scale.den = ifce->timescale; | |
| } | |
| return stream; | |
| } | |
| #if 0 //unused | |
| 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; | |
| } | |
| #endif | |
| 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 | |
| u32 gf_m2ts_mux_program_count(GF_M2TS_Mux *muxer) | |
| { | |
| u32 num=0; | |
| GF_M2TS_Mux_Program *program = muxer->programs; | |
| while (program) { | |
| num++; | |
| program = program->next; | |
| } | |
| return num; | |
| } | |
| 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, u64 pcr_offset, u32 mpeg4_signaling, u32 pmt_version, Bool initial_disc, u64 force_first_pts) | |
| { | |
| 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; | |
| program->pmt->initial_version_number = pmt_version; | |
| program->initial_disc_set = initial_disc; | |
| program->pmt->set_initial_disc = initial_disc; | |
| muxer->pat->table_needs_update = GF_TRUE; | |
| program->pmt->process = gf_m2ts_stream_process_pmt; | |
| program->force_first_pts = force_first_pts; | |
| 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; | |
| } | |
| void gf_m2ts_mux_program_force_keep_ts(GF_M2TS_Mux_Program *program) | |
| { | |
| if (program) { | |
| program->initial_ts_set = 2; | |
| program->initial_ts = 0; | |
| } | |
| } | |
| GF_EXPORT | |
| GF_M2TS_Mux *gf_m2ts_mux_new(u32 mux_rate, u32 pat_refresh_rate, Bool real_time) | |
| { | |
| 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; | |
| /*format NULL packet*/ | |
| muxer->pck_bs = gf_bs_new(muxer->null_pck, 188, GF_BITSTREAM_WRITE); | |
| gf_bs_write_int(muxer->pck_bs, 0x47, 8); | |
| gf_bs_write_int(muxer->pck_bs, 0, 1); | |
| gf_bs_write_int(muxer->pck_bs, 0, 1); | |
| gf_bs_write_int(muxer->pck_bs, 0, 1); | |
| gf_bs_write_int(muxer->pck_bs, 0x1FFF, 13); | |
| gf_bs_write_int(muxer->pck_bs, 0, 2); | |
| gf_bs_write_int(muxer->pck_bs, 1, 2); | |
| gf_bs_write_int(muxer->pck_bs, 0, 4); | |
| 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->curr_pck.mpeg2_af_descriptors) gf_free(st->curr_pck.mpeg2_af_descriptors); | |
| 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) | |
| { | |
| if (prog->iod) { | |
| gf_odf_desc_del(prog->iod); | |
| } | |
| 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_program_stream_remove(GF_M2TS_Mux_Stream *stream) | |
| { | |
| GF_M2TS_Mux_Program *program = stream->program; | |
| GF_M2TS_Mux_Stream *a_stream = program->streams; | |
| if (a_stream==stream) { | |
| program->streams = stream->next; | |
| } else { | |
| while (a_stream) { | |
| if (a_stream->next == stream) { | |
| a_stream->next = stream->next; | |
| break; | |
| } | |
| a_stream = a_stream->next; | |
| } | |
| } | |
| stream->next = NULL; | |
| //if we remove PCR, reassign to first stream | |
| if (program->pcr == stream) | |
| program->pcr = program->streams; | |
| gf_m2ts_mux_stream_del(stream); | |
| program->pmt->table_needs_update = GF_TRUE; | |
| } | |
| 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); | |
| if (mux->pck_bs) gf_bs_del(mux->pck_bs); | |
| 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; | |
| /*get PAT bitrate*/ | |
| 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; | |
| } | |
| /*reset mux time*/ | |
| if (reset_time) stream->time.sec = stream->time.nanosec = 0; | |
| stream = stream->next; | |
| } | |
| /*get PMT bitrate*/ | |
| gf_m2ts_mux_table_update_bitrate(mux, prog->pmt); | |
| if (!mux->fixed_rate) { | |
| mux->bit_rate += prog->pmt->bit_rate; | |
| } | |
| prog = prog->next; | |
| } | |
| /*reset mux time*/ | |
| if (reset_time) { | |
| mux->time.sec = mux->time.nanosec = 0; | |
| mux->init_sys_time = 0; | |
| } | |
| if (!mux->bit_rate) { | |
| GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG2-TS Muxer] No bitrates set in VBR mux mode, using 100kbps\n")); | |
| mux->bit_rate=100000; | |
| } | |
| } | |
| 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 | |
| u64 gf_m2ts_get_ts_clock_90k(GF_M2TS_Mux *muxer) | |
| { | |
| u64 now, init; | |
| init = ((u64)muxer->init_ts_time.sec) * 90000; | |
| init += ((u64) muxer->init_ts_time.nanosec/1000) * 9 / 100; | |
| now = ((u64) muxer->time.sec)*90000; | |
| now += ((u64) muxer->time.nanosec/1000) * 9 / 100; | |
| 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; | |
| } | |
| /* | |
| The algo in gf_m2ts_mux_process elects the first stream with lowest time but uses +INF as initial time | |
| when no realtime nor fixed rate. Regulation is then needed because input may arrive at any time, | |
| so one stream S1 with input TS1 may be elected to be sent | |
| while other streams are still waiting for inputs with TSX < TS1. Not regulating would send S1 way too fast. | |
| This only applies for non real time VBR | |
| We therefore need to check if the stream time is not too ahead of the current mux time. | |
| However, since the mux time in this mode is set to the stream time of the last stream sent, | |
| we also need to check the PCR stream: | |
| - if over, flush everything | |
| - otherwise, if all non-PCR streams are done or have a stream time >= PCR stream time, the PCR stream can be sent | |
| - otherwise, the PCR will wait until all non-PCR streams clocks have reached the PCR stream clock | |
| */ | |
| static Bool gf_m2ts_stream_too_early(GF_M2TS_Mux_Stream *stream, GF_M2TS_Mux *muxer) | |
| { | |
| GF_M2TS_Time t; | |
| if (muxer->fixed_rate || muxer->real_time) return GF_FALSE; | |
| //any stream behind the PCR is not too early | |
| if (gf_m2ts_time_less(&stream->time, &stream->program->pcr->time)) | |
| return GF_FALSE; | |
| //we consider sending a stream 100ms ahead of current mux time is too early | |
| //this is independent of the mux type (realtime, fixed rate) | |
| t = stream->time; | |
| gf_m2ts_time_inc(&t, 100, 1000); | |
| if (!gf_m2ts_time_less(&muxer->time, &t)) { | |
| return GF_FALSE; | |
| } | |
| //if PCR stream is over, flush | |
| if (stream->program->pcr->ifce) { | |
| if (stream->program->pcr->ifce->caps & (GF_ESI_STREAM_FLUSH|GF_ESI_STREAM_IS_OVER|GF_ESI_STREAM_SPARSE)) | |
| return GF_FALSE; | |
| } | |
| //PCR is in pure pcr mode (no associated data), check is any stream is less than PCR | |
| if (stream->program->pcr->pcr_only_mode) | |
| stream = stream->program->pcr; | |
| //if this is the PCR, wait only if some streams in program are behind us | |
| if (stream->program->pcr == stream) { | |
| GF_M2TS_Mux_Stream *a_stream = stream->program->streams; | |
| while (a_stream) { | |
| //a_stream time less than stream time, we must wait on stream to process a_stream | |
| if ((a_stream != stream) | |
| && a_stream->ifce && !(a_stream->ifce->caps & (GF_ESI_STREAM_FLUSH|GF_ESI_STREAM_IS_OVER|GF_ESI_STREAM_SPARSE)) | |
| && gf_m2ts_time_less(&a_stream->time, &stream->time) | |
| ) { | |
| return GF_TRUE; | |
| } | |
| a_stream = a_stream->next; | |
| } | |
| return GF_FALSE; | |
| } | |
| //PCR done or flush, we're not too early | |
| else if (stream->program->pcr->ifce && (stream->program->pcr->ifce->caps & (GF_ESI_STREAM_FLUSH|GF_ESI_STREAM_IS_OVER|GF_ESI_STREAM_SPARSE))) | |
| return GF_FALSE; | |
| //not PCR, we're too early | |
| return GF_TRUE; | |
| } | |
| GF_EXPORT | |
| const u8 *gf_m2ts_mux_process(GF_M2TS_Mux *muxer, GF_M2TSMuxState *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; | |
| muxer->sap_inserted = 0; | |
| muxer->last_pid = 0; | |
| now_us = gf_sys_clock_high_res(); | |
| if (muxer->real_time) { | |
| if (!muxer->init_sys_time) { | |
| //init TS 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 { | |
| gf_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; | |
| /*bitrate have changed*/ | |
| 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) { | |
| /*compare PAT and PMT with current mux time | |
| if non-fixed rate, current mux time is the time of the last packet sent, time test is still valid - it will however not work | |
| if min access unit duration from all streams is greater than the PSI refresh rate*/ | |
| /*PAT*/ | |
| res = muxer->pat->process(muxer, muxer->pat); | |
| if ((res && gf_m2ts_time_less_or_equal(&muxer->pat->time, &time)) | |
| || muxer->pat->table_needs_send | |
| || 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; | |
| } | |
| /*force sending the PAT regardless of other streams*/ | |
| goto send_pck; | |
| } | |
| /*SDT*/ | |
| 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; | |
| } | |
| } | |
| /*PMT, for each program*/ | |
| program = muxer->programs; | |
| while (program) { | |
| res = program->pmt->process(muxer, program->pmt); | |
| if ((res && gf_m2ts_time_less_or_equal(&program->pmt->time, &time)) | |
| || program->pmt->table_needs_send | |
| || (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; | |
| /*force sending the PMT regardless of other streams*/ | |
| goto send_pck; | |
| } | |
| program = program->next; | |
| } | |
| } | |
| /*if non-fixed rate, just pick the earliest data on all streams*/ | |
| 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 | |
| /*PCR stream, for each program (send them first to avoid PCR to never be sent)*/ | |
| 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 | |
| /*all streams for each program*/ | |
| highest_priority = 0; | |
| program = muxer->programs; | |
| while (program) { | |
| stream = program->streams; | |
| //first pass, process all PES and update stream clocks - this is needed for non realtime VBR because gf_m2ts_stream_too_early (below) assumes all | |
| //stream times are up to date | |
| while (stream) { | |
| if (flush_all_pes && !stream->pes_data_remain) { | |
| stream->process_res = 0; | |
| } else { | |
| stream->process_res = stream->process(muxer, stream); | |
| } | |
| stream = stream->next; | |
| continue; | |
| } | |
| //second pass, get earliest PES packet | |
| 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_res; | |
| /*next is rap on this stream, check flushing of other pes (we could use a goto)*/ | |
| if (!flush_all_pes && muxer->force_pat) | |
| return gf_m2ts_mux_process(muxer, status, usec_till_next); | |
| if (res) { | |
| /*always schedule the earliest data*/ | |
| if (gf_m2ts_time_less(&stream->time, &time) | |
| //we must regulate as input may arrive in burst | |
| && (flush_all_pes || !gf_m2ts_stream_too_early(stream, muxer)) | |
| ) { | |
| 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 the same priority schedule base stream first*/ | |
| 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) && !stream->pes_data_remain) | |
| 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; | |
| } | |
| /* padding packets ?? */ | |
| 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(muxer, stream_to_process, muxer->dst_pck); | |
| } else { | |
| gf_m2ts_mux_pes_get_next_packet(stream_to_process, muxer->dst_pck); | |
| if (stream_to_process->pid == muxer->ref_pid) { | |
| if (stream_to_process->pck_sap_type) { | |
| muxer->sap_inserted = GF_TRUE; | |
| muxer->sap_type = stream_to_process->pck_sap_type; | |
| muxer->sap_time = stream_to_process->pck_sap_time; | |
| } | |
| muxer->last_pts = stream_to_process->curr_pck.cts; | |
| muxer->last_pid = stream_to_process->pid; | |
| } | |
| } | |
| 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++; | |
| /*increment time*/ | |
| if (muxer->fixed_rate ) { | |
| gf_m2ts_time_inc(&muxer->time, 1504/*188*8*/, 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); | |
| } | |
| /*if a stream was found, use it - regulation is done by gf_m2ts_stream_too_early*/ | |
| else if (stream_to_process) { | |
| if (gf_m2ts_time_less_or_equal(&muxer->time, &time)) | |
| 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) { | |
| // 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); | |
| muxer->time = max_time; | |
| } | |
| return ret; | |
| } | |
| #endif /*GPAC_DISABLE_MPEG2TS_MUX*/ |