blob: 66f464137e61218110e83505addf7006857319ab [file] [edit]
/*
* GPAC - Multimedia Framework C SDK
*
* Authors: Jean Le Feuvre
* Copyright (c) Telecom ParisTech 2005-2026
*
* This file is part of GPAC / MPEG2-TS 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>
#ifndef GPAC_DISABLE_MPEG2TS
#include <string.h>
#include <gpac/constants.h>
#include <gpac/internal/media_dev.h>
#include <gpac/download.h>
#ifndef GPAC_DISABLE_STREAMING
#include <gpac/internal/ietf_dev.h>
#endif
#ifdef GPAC_CONFIG_LINUX
#include <unistd.h>
#endif
#ifdef GPAC_ENABLE_MPE
#include <gpac/dvb_mpe.h>
#endif
#ifdef GPAC_ENABLE_DSMCC
#include <gpac/ait.h>
#endif
#define DEBUG_TS_PACKET 0
GF_EXPORT
const char *gf_m2ts_get_stream_name(GF_M2TSStreamType streamType)
{
switch (streamType) {
case GF_M2TS_VIDEO_MPEG1:
return "MPEG-1 Video";
case GF_M2TS_VIDEO_MPEG2:
return "MPEG-2 Video";
case GF_M2TS_AUDIO_MPEG1:
return "MPEG-1 Audio";
case GF_M2TS_AUDIO_MPEG2:
return "MPEG-2 Audio";
case GF_M2TS_PRIVATE_SECTION:
return "Private Section";
case GF_M2TS_PRIVATE_DATA:
return "Private Data";
case GF_M2TS_AUDIO_AAC:
return "AAC Audio";
case GF_M2TS_VIDEO_MPEG4:
return "MPEG-4 Video";
case GF_M2TS_VIDEO_H264:
return "MPEG-4/H264 Video";
case GF_M2TS_VIDEO_SVC:
return "H264-SVC Video";
case GF_M2TS_VIDEO_HEVC:
return "HEVC Video";
case GF_M2TS_VIDEO_SHVC:
return "SHVC Video";
case GF_M2TS_VIDEO_SHVC_TEMPORAL:
return "SHVC Video Temporal Sublayer";
case GF_M2TS_VIDEO_MHVC:
return "MHVC Video";
case GF_M2TS_VIDEO_MHVC_TEMPORAL:
return "MHVC Video Temporal Sublayer";
case GF_M2TS_VIDEO_VVC:
return "VVC Video";
case GF_M2TS_VIDEO_VVC_TEMPORAL:
return "VVC Video Temporal Sublayer";
case GF_M2TS_VIDEO_VC1:
return "SMPTE VC-1 Video";
case GF_M2TS_AUDIO_AC3:
return "Dolby AC3 Audio";
case GF_M2TS_AUDIO_EC3:
return "Dolby E-AC3 Audio";
case GF_M2TS_AUDIO_DTS:
return "Dolby DTS Audio";
case GF_M2TS_SUBTITLE_DVB:
return "DVB Subtitle";
case GF_M2TS_SYSTEMS_MPEG4_PES:
return "MPEG-4 SL (PES)";
case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
return "MPEG-4 SL (Section)";
case GF_M2TS_MPE_SECTIONS:
return "MPE (Section)";
case GF_M2TS_METADATA_PES:
return "Metadata (PES)";
case GF_M2TS_METADATA_ID3_HLS:
return "ID3/HLS Metadata (PES)";
case GF_M2TS_METADATA_ID3_KLVA:
return "ID3/KLV Metadata (PES)";
case GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS:
return "SCTE35 splice_info_section (Section)";
default:
return "Unknown";
}
}
static u32 gf_m2ts_reframe_default(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len, GF_M2TS_PESHeader *pes_hdr)
{
GF_M2TS_PES_PCK pck;
pck.flags = 0;
if (pes->rap) pck.flags |= GF_M2TS_PES_PCK_RAP;
if (!same_pts) pck.flags |= GF_M2TS_PES_PCK_AU_START;
pck.DTS = pes->DTS;
pck.PTS = pes->PTS;
pck.data = (char *)data;
pck.data_len = data_len;
pck.stream = pes;
ts->on_event(ts, GF_M2TS_EVT_PES_PCK, &pck);
/*we consumed all data*/
return 0;
}
static u32 gf_m2ts_reframe_reset(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len, GF_M2TS_PESHeader *pes_hdr)
{
if (pes->pck_data) {
gf_free(pes->pck_data);
pes->pck_data = NULL;
}
pes->pck_data_len = pes->pck_alloc_len = 0;
if (pes->prev_data) {
gf_free(pes->prev_data);
pes->prev_data = NULL;
}
pes->prev_data_len = 0;
pes->pes_len = 0;
pes->reframe = NULL;
pes->cc = -1;
pes->temi_tc_desc_len = 0;
return 0;
}
static u32 gf_m2ts_reframe_add_prop(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, Bool same_pts, unsigned char *data, u32 data_len, GF_M2TS_PESHeader *pes_hdr)
{
GF_M2TS_PES_PCK pck;
pck.flags = 0;
pck.DTS = pes->DTS;
pck.PTS = pes->PTS;
pck.data = (char *)data;
pck.data_len = data_len;
pck.stream = pes;
pck.stream->stream_type = pes->stream_type;
ts->on_event(ts, GF_M2TS_EVT_ID3/*should depend on pes->streamtype*/, &pck);
return 0;
}
static u32 gf_m2ts_sync(GF_M2TS_Demuxer *ts, char *data, u32 size, Bool simple_check)
{
u32 i=0;
/*if first byte is sync assume we're sync*/
if (simple_check && (data[i]==0x47)) return 0;
while (i < size) {
if (i+192 >= size) return size;
if ((data[i]==0x47) && (data[i+188]==0x47))
break;
if ((data[i]==0x47) && (data[i+192]==0x47)) {
ts->prefix_present = 1;
break;
}
i++;
}
if (i) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] re-sync skipped %d bytes\n", i) );
}
return i;
}
Bool gf_m2ts_crc32_check(u8 *data, u32 len)
{
u32 crc = gf_crc_32(data, len);
u32 crc_val = GF_4CC((u32) data[len], (u8) data[len+1], (u8) data[len+2], (u8) data[len+3]);
return (crc==crc_val) ? GF_TRUE : GF_FALSE;
}
static GF_M2TS_SectionFilter *gf_m2ts_section_filter_new(gf_m2ts_section_callback process_section_callback, Bool process_individual)
{
GF_M2TS_SectionFilter *sec;
GF_SAFEALLOC(sec, GF_M2TS_SectionFilter);
if (!sec) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] gf_m2ts_section_filter_new : OUT OF MEMORY\n"));
return NULL;
}
sec->cc = -1;
sec->process_section = process_section_callback;
sec->process_individual = process_individual;
return sec;
}
static void gf_m2ts_reset_sections(GF_List *sections)
{
u32 count;
//GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Deleting sections\n"));
count = gf_list_count(sections);
while (count) {
GF_M2TS_Section *section = gf_list_get(sections, 0);
gf_list_rem(sections, 0);
if (section->data) gf_free(section->data);
gf_free(section);
count--;
}
}
static void gf_m2ts_section_filter_reset(GF_M2TS_SectionFilter *sf)
{
if (sf->section) {
gf_free(sf->section);
sf->section = NULL;
}
while (sf->table) {
GF_M2TS_Table *t = sf->table;
sf->table = t->next;
gf_m2ts_reset_sections(t->sections);
gf_list_del(t->sections);
gf_free(t);
}
sf->cc = -1;
sf->length = sf->received = 0;
sf->demux_restarted = 1;
}
static void gf_m2ts_section_filter_del(GF_M2TS_SectionFilter *sf)
{
gf_m2ts_section_filter_reset(sf);
gf_free(sf);
}
static void gf_m2ts_metadata_descriptor_del(GF_M2TS_MetadataDescriptor *metad)
{
if (metad) {
if (metad->service_id_record) gf_free(metad->service_id_record);
if (metad->decoder_config) gf_free(metad->decoder_config);
if (metad->decoder_config_id) gf_free(metad->decoder_config_id);
gf_free(metad);
}
}
static void gf_m2ts_es_del(GF_M2TS_ES *es, GF_M2TS_Demuxer *ts)
{
//es is reused (PCR streams only, reuse at most one), remove reuse flag
if (es->flags & GF_M2TS_ES_IS_PCR_REUSE) {
es->flags &= ~GF_M2TS_ES_IS_PCR_REUSE;
return;
}
gf_list_del_item(es->program->streams, es);
if (ts->on_event)
ts->on_event(ts, GF_M2TS_EVT_STREAM_REMOVED, es);
if (es->flags & GF_M2TS_ES_IS_SECTION) {
GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es;
if (ses->sec) gf_m2ts_section_filter_del(ses->sec);
#ifdef GPAC_ENABLE_MPE
if (es->flags & GF_M2TS_ES_IS_MPE)
gf_dvb_mpe_section_del(es);
#endif
} else if (es->pid!=es->program->pmt_pid) {
GF_M2TS_PES *pes = (GF_M2TS_PES *)es;
if ((pes->flags & GF_M2TS_INHERIT_PCR) && ts->ess[es->program->pcr_pid]==es)
ts->ess[es->program->pcr_pid] = NULL;
if (pes->pck_data) gf_free(pes->pck_data);
if (pes->prev_data) gf_free(pes->prev_data);
if (pes->temi_tc_desc) gf_free(pes->temi_tc_desc);
if (pes->metadata_descriptor) gf_m2ts_metadata_descriptor_del(pes->metadata_descriptor);
if (pes->gpac_meta_dsi) gf_free(pes->gpac_meta_dsi);
}
if (es->slcfg) gf_free(es->slcfg);
for (u32 i=0; i<GF_M2TS_MAX_STREAMS; i++) {
if (ts->ess[i]==es) {
ts->ess[i] = NULL;
}
}
gf_free(es);
}
static void gf_m2ts_reset_sdt(GF_M2TS_Demuxer *ts)
{
while (gf_list_count(ts->SDTs)) {
GF_M2TS_SDT *sdt = (GF_M2TS_SDT *)gf_list_last(ts->SDTs);
gf_list_rem_last(ts->SDTs);
if (sdt->provider) gf_free(sdt->provider);
if (sdt->service) gf_free(sdt->service);
gf_free(sdt);
}
}
GF_EXPORT
GF_M2TS_SDT *gf_m2ts_get_sdt_info(GF_M2TS_Demuxer *ts, u32 program_id)
{
u32 i;
for (i=0; i<gf_list_count(ts->SDTs); i++) {
GF_M2TS_SDT *sdt = (GF_M2TS_SDT *)gf_list_get(ts->SDTs, i);
if (sdt->service_id==program_id) return sdt;
}
return NULL;
}
static void gf_m2ts_section_complete(GF_M2TS_Demuxer *ts, GF_M2TS_SectionFilter *sec, GF_M2TS_SECTION_ES *ses)
{
//seek mode, only process PAT and PMT
if (ts->seek_mode && (sec->section[0] != GF_M2TS_TABLE_ID_PAT) && (sec->section[0] != GF_M2TS_TABLE_ID_PMT)) {
/*clean-up (including broken sections)*/
if (sec->section) gf_free(sec->section);
sec->section = NULL;
sec->length = sec->received = 0;
return;
}
if (!sec->process_section) {
if ((ts->on_event && (sec->section[0]==GF_M2TS_TABLE_ID_AIT)) ) {
#ifdef GPAC_ENABLE_DSMCC
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
//ts->on_event(ts, GF_M2TS_EVT_AIT_FOUND, &pck);
on_ait_section(ts, GF_M2TS_EVT_AIT_FOUND, &pck);
#endif
} else if ((ts->on_event && (sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_ENCAPSULATED_DATA || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE ||
sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_STREAM_DESCRIPTION || sec->section[0]==GF_M2TS_TABLE_ID_DSM_CC_PRIVATE)) ) {
#ifdef GPAC_ENABLE_DSMCC
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
on_dsmcc_section(ts,GF_M2TS_EVT_DSMCC_FOUND,&pck);
//ts->on_event(ts, GF_M2TS_EVT_DSMCC_FOUND, &pck);
#endif
}
#ifdef GPAC_ENABLE_MPE
else if (ts->on_mpe_event && ((ses && (ses->flags & GF_M2TS_EVT_DVB_MPE)) || (sec->section[0]==GF_M2TS_TABLE_ID_INT)) ) {
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
ts->on_mpe_event(ts, GF_M2TS_EVT_DVB_MPE, &pck);
}
#endif
else if ((ts->on_event && (sec->section[0]==GF_M2TS_TABLE_ID_SCTE35_SPLICE_INFO)) ) {
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
ts->on_event(ts, GF_M2TS_EVT_SCTE35_SPLICE_INFO, &pck);
}
else if (ts->on_event) {
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
ts->on_event(ts, GF_M2TS_EVT_DVB_GENERAL, &pck);
}
} else {
Bool has_syntax_indicator;
u8 table_id;
u16 extended_table_id;
u32 status, section_start, i;
GF_M2TS_Table *t, *prev_t;
unsigned char *data;
Bool section_valid = 0;
status = 0;
/*parse header*/
data = (u8 *)sec->section;
if (sec->length < 2) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] M2TS Table parsing error, length %d is too small\n", sec->length) );
return;
}
/*look for proper table*/
table_id = data[0];
if (ts->on_event) {
switch (table_id) {
case GF_M2TS_TABLE_ID_PAT:
case GF_M2TS_TABLE_ID_SDT_ACTUAL:
case GF_M2TS_TABLE_ID_PMT:
case GF_M2TS_TABLE_ID_NIT_ACTUAL:
case GF_M2TS_TABLE_ID_TDT:
case GF_M2TS_TABLE_ID_TOT:
{
GF_M2TS_SL_PCK pck;
pck.data_len = sec->length;
pck.data = sec->section;
pck.stream = (GF_M2TS_ES *)ses;
ts->on_event(ts, GF_M2TS_EVT_DVB_GENERAL, &pck);
}
}
}
has_syntax_indicator = (data[1] & 0x80) ? 1 : 0;
if (has_syntax_indicator) {
if (sec->length < 5) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] M2TS Table parsing error, length %d is too small\n", sec->length) );
return;
}
extended_table_id = (data[3]<<8) | data[4];
} else {
extended_table_id = 0;
}
prev_t = NULL;
t = sec->table;
while (t) {
if ((t->table_id==table_id) && (t->ex_table_id == extended_table_id)) break;
prev_t = t;
t = t->next;
}
/*create table*/
if (!t) {
GF_SAFEALLOC(t, GF_M2TS_Table);
if (!t) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Fail to alloc table %d %d\n", table_id, extended_table_id));
return;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Creating table %d %d\n", table_id, extended_table_id));
t->table_id = table_id;
t->ex_table_id = extended_table_id;
t->last_version_number = 0xFF;
t->sections = gf_list_new();
if (prev_t) prev_t->next = t;
else sec->table = t;
}
if (has_syntax_indicator) {
if (sec->length < 4) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted section length %d less than CRC \n", sec->length));
} else {
/*remove crc32*/
sec->length -= 4;
if (gf_m2ts_crc32_check((char *)data, sec->length)) {
s32 cur_sec_num;
t->version_number = (data[5] >> 1) & 0x1f;
if (t->last_section_number && t->section_number && (t->version_number != t->last_version_number)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] table transmission interrupted: previous table (v=%d) %d/%d sections - new table (v=%d) %d/%d sections\n", t->last_version_number, t->section_number, t->last_section_number, t->version_number, data[6] + 1, data[7] + 1) );
gf_m2ts_reset_sections(t->sections);
t->section_number = 0;
}
t->current_next_indicator = (data[5] & 0x1) ? 1 : 0;
/*add one to section numbers to detect if we missed or not the first section in the table*/
cur_sec_num = data[6] + 1;
t->last_section_number = data[7] + 1;
section_start = 8;
/*we missed something*/
if (!sec->process_individual && t->section_number + 1 != cur_sec_num) {
/* TODO - Check how to handle sections when the first complete section does
not have its sec num 0 */
section_valid = 0;
if (t->is_init) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted table (lost section %d)\n", cur_sec_num ? cur_sec_num-1 : 31) );
}
} else {
section_valid = 1;
t->section_number = cur_sec_num;
}
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted section (CRC32 failed)\n"));
}
}
} else {
section_valid = 1;
section_start = 3;
}
/*process section*/
if (section_valid && sec->length > section_start) {
GF_M2TS_Section *section;
GF_SAFEALLOC(section, GF_M2TS_Section);
if (!section) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Fail to create section\n"));
return;
}
section->data_size = sec->length - section_start;
section->data = (unsigned char*)gf_malloc(sizeof(unsigned char)*section->data_size);
memcpy(section->data, sec->section + section_start, sizeof(unsigned char)*section->data_size);
gf_list_add(t->sections, section);
if (t->section_number == 1) {
status |= GF_M2TS_TABLE_START;
if (t->last_version_number == t->version_number) {
t->is_repeat = 1;
} else {
t->is_repeat = 0;
}
/*only update version number in the first section of the table*/
t->last_version_number = t->version_number;
}
if (t->is_init) {
if (t->is_repeat) {
status |= GF_M2TS_TABLE_REPEAT;
} else {
status |= GF_M2TS_TABLE_UPDATE;
}
} else {
status |= GF_M2TS_TABLE_FOUND;
}
if (t->last_section_number == t->section_number) {
u32 table_size;
status |= GF_M2TS_TABLE_END;
table_size = 0;
for (i=0; i<gf_list_count(t->sections); i++) {
GF_M2TS_Section *a_sec = gf_list_get(t->sections, i);
table_size += a_sec->data_size;
}
if (t->is_repeat) {
if (t->table_size != table_size) {
status |= GF_M2TS_TABLE_UPDATE;
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Repeated section found with different sizes (old table %d bytes, new table %d bytes)\n", t->table_size, table_size) );
t->table_size = table_size;
}
} else {
t->table_size = table_size;
}
t->is_init = 1;
/*reset section number*/
t->section_number = 0;
t->is_repeat = 0;
}
if (sec->process_individual) {
/*send each section of the table and not the aggregated table*/
if (sec->process_section)
sec->process_section(ts, ses, t->sections, t->table_id, t->ex_table_id, t->version_number, (u8) (t->last_section_number - 1), status);
gf_m2ts_reset_sections(t->sections);
} else {
if (status&GF_M2TS_TABLE_END) {
if (sec->process_section)
sec->process_section(ts, ses, t->sections, t->table_id, t->ex_table_id, t->version_number, (u8) (t->last_section_number - 1), status);
gf_m2ts_reset_sections(t->sections);
}
}
} else {
sec->cc = -1;
t->section_number = 0;
}
}
/*clean-up (including broken sections)*/
if (sec->section) gf_free(sec->section);
sec->section = NULL;
sec->length = sec->received = 0;
}
static Bool gf_m2ts_is_long_section(u8 table_id)
{
switch (table_id) {
case GF_M2TS_TABLE_ID_MPEG4_BIFS:
case GF_M2TS_TABLE_ID_MPEG4_OD:
case GF_M2TS_TABLE_ID_INT:
case GF_M2TS_TABLE_ID_EIT_ACTUAL_PF:
case GF_M2TS_TABLE_ID_EIT_OTHER_PF:
case GF_M2TS_TABLE_ID_ST:
case GF_M2TS_TABLE_ID_SIT:
case GF_M2TS_TABLE_ID_DSM_CC_PRIVATE:
case GF_M2TS_TABLE_ID_MPE_FEC:
case GF_M2TS_TABLE_ID_DSM_CC_DOWNLOAD_DATA_MESSAGE:
case GF_M2TS_TABLE_ID_DSM_CC_UN_MESSAGE:
return 1;
default:
if (table_id >= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MIN && table_id <= GF_M2TS_TABLE_ID_EIT_SCHEDULE_MAX)
return 1;
else
return 0;
}
}
static u32 gf_m2ts_get_section_length(char byte0, char byte1, char byte2)
{
u32 length;
if (gf_m2ts_is_long_section(byte0)) {
length = 3 + ( (((u8)byte1<<8) | (byte2&0xff)) & 0xfff );
} else {
length = 3 + ( (((u8)byte1<<8) | (byte2&0xff)) & 0x3ff );
}
return length;
}
static void gf_m2ts_gather_section(GF_M2TS_Demuxer *ts, GF_M2TS_SectionFilter *sec, GF_M2TS_SECTION_ES *ses, GF_M2TS_Header *hdr, unsigned char *data, u32 data_size)
{
u32 payload_size = data_size;
u8 expect_cc = (sec->cc<0) ? hdr->continuity_counter : (sec->cc + 1) & 0xf;
Bool disc = (expect_cc == hdr->continuity_counter) ? 0 : 1;
sec->cc = expect_cc;
/*may happen if hdr->adaptation_field=2 no payload in TS packet*/
if (!data_size) return;
if (hdr->payload_start) {
u32 ptr_field;
ptr_field = data[0];
if (ptr_field+1>data_size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid section start (@ptr_field=%d, @data_size=%d)\n", ptr_field, data_size) );
return;
}
if (!hdr->pid)
ts->last_pat_start_num = ts->pck_number-1;
/*end of previous section*/
if (!sec->length && sec->received) {
/* the length of the section could not be determined from the previous TS packet because we had only 1 or 2 bytes */
if (sec->received == 1)
sec->length = gf_m2ts_get_section_length(sec->section[0], data[1], data[2]);
else /* (sec->received == 2) */
sec->length = gf_m2ts_get_section_length(sec->section[0], sec->section[1], data[1]);
sec->section = (char*)gf_realloc(sec->section, sizeof(char)*sec->length);
}
if (sec->length && (sec->received < sec->length) && (data_size >= (u32) (1 + sec->length - sec->received))) {
u32 len = sec->length - sec->received;
memcpy(sec->section + sec->received, data+1, sizeof(char)*len);
sec->received += len;
if (ptr_field > len)
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid pointer field (@ptr_field=%d, @remaining=%d)\n", ptr_field, len) );
gf_m2ts_section_complete(ts, sec, ses);
}
data += ptr_field+1;
data_size -= ptr_field+1;
payload_size -= ptr_field+1;
aggregated_section:
if (sec->section) gf_free(sec->section);
sec->length = sec->received = 0;
sec->section = (char*)gf_malloc(sizeof(char)*data_size);
memcpy(sec->section, data, sizeof(char)*data_size);
sec->received = data_size;
} else if (disc) {
if (sec->section) gf_free(sec->section);
sec->section = NULL;
sec->received = sec->length = 0;
return;
} else if (!sec->section) {
return;
} else {
if (sec->length && sec->received+data_size > sec->length)
data_size = sec->length - sec->received;
if (sec->length) {
memcpy(sec->section + sec->received, data, sizeof(char)*data_size);
} else {
sec->section = (char*)gf_realloc(sec->section, sizeof(char)*(sec->received+data_size));
memcpy(sec->section + sec->received, data, sizeof(char)*data_size);
}
sec->received += data_size;
}
/*alloc final buffer*/
if (!sec->length && (sec->received >= 3)) {
sec->length = gf_m2ts_get_section_length(sec->section[0], sec->section[1], sec->section[2]);
sec->section = (char*)gf_realloc(sec->section, sizeof(char)*sec->length);
if (sec->received > sec->length) {
data_size -= sec->received - sec->length;
sec->received = sec->length;
}
}
if (!sec->length || sec->received < sec->length) return;
/*OK done*/
gf_m2ts_section_complete(ts, sec, ses);
if (payload_size > data_size) {
data += data_size;
/* detect padding after previous section */
if (data[0] != 0xFF) {
data_size = payload_size - data_size;
payload_size = data_size;
goto aggregated_section;
}
}
}
static void gf_m2ts_process_sdt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
u32 pos, evt_type;
u32 nb_sections;
u32 data_size;
unsigned char *data;
GF_M2TS_Section *section;
/*wait for the last section */
if (!(status&GF_M2TS_TABLE_END)) return;
/*skip if already received*/
if (status&GF_M2TS_TABLE_REPEAT) {
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SDT_REPEAT, NULL);
return;
}
if (table_id != GF_M2TS_TABLE_ID_SDT_ACTUAL) {
return;
}
gf_m2ts_reset_sdt(ts);
nb_sections = gf_list_count(sections);
if (nb_sections > 1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] SDT on multiple sections not supported\n"));
}
section = (GF_M2TS_Section *)gf_list_get(sections, 0);
data = section->data;
data_size = section->data_size;
//orig_net_id = (data[0] << 8) | data[1];
pos = 3;
while (pos+4 < data_size) {
GF_M2TS_SDT *sdt;
u32 descs_size, d_pos, ulen;
GF_SAFEALLOC(sdt, GF_M2TS_SDT);
if (!sdt) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Fail to create SDT\n"));
return;
}
gf_list_add(ts->SDTs, sdt);
sdt->service_id = (data[pos]<<8) + data[pos+1];
sdt->EIT_schedule = (data[pos+2] & 0x2) ? 1 : 0;
sdt->EIT_present_following = (data[pos+2] & 0x1);
sdt->running_status = (data[pos+3]>>5) & 0x7;
sdt->free_CA_mode = (data[pos+3]>>4) & 0x1;
descs_size = ((data[pos+3]&0xf)<<8) | data[pos+4];
pos += 5;
if (pos+descs_size > data_size) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid descriptors size read from data (%u)\n", descs_size));
return;
}
d_pos = 0;
while (d_pos+1 < descs_size) {
u8 d_tag = data[pos+d_pos];
u8 d_len = data[pos+d_pos+1];
switch (d_tag) {
case GF_M2TS_DVB_SERVICE_DESCRIPTOR:
if (sdt->provider) gf_free(sdt->provider);
sdt->provider = NULL;
if (sdt->service) gf_free(sdt->service);
sdt->service = NULL;
d_pos+=2;
if (pos+d_pos+1 >= data_size) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid descriptors size read from data (%u)\n", descs_size));
return;
}
sdt->service_type = data[pos+d_pos];
ulen = data[pos+d_pos+1];
d_pos += 2;
if (pos+d_pos+ulen > data_size) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid descriptors size read from data (%u)\n", descs_size));
return;
}
sdt->provider = (char*)gf_malloc(sizeof(char)*(ulen+1));
memcpy(sdt->provider, data+pos+d_pos, sizeof(char)*ulen);
sdt->provider[ulen] = 0;
d_pos += ulen;
if (pos+d_pos >= data_size) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid descriptors size read from data (%u)\n", descs_size));
gf_free(sdt->provider);
sdt->provider = NULL;
return;
}
ulen = data[pos+d_pos];
d_pos += 1;
if (pos+d_pos+ulen > data_size) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid descriptors size read from data (%u)\n", descs_size));
gf_free(sdt->provider);
sdt->provider = NULL;
return;
}
sdt->service = (char*)gf_malloc(sizeof(char)*(ulen+1));
memcpy(sdt->service, data+pos+d_pos, sizeof(char)*ulen);
sdt->service[ulen] = 0;
d_pos += ulen;
break;
default:
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Skipping descriptor (0x%x) not supported\n", d_tag));
d_pos += d_len;
if (d_len == 0) d_pos = descs_size;
break;
}
}
pos += descs_size;
}
evt_type = GF_M2TS_EVT_SDT_FOUND;
if (ts->on_event) ts->on_event(ts, evt_type, NULL);
}
static void gf_m2ts_process_mpeg4section(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
GF_M2TS_SL_PCK sl_pck;
u32 nb_sections, i;
GF_M2TS_Section *section;
//only process MPEG4 tables
if ((table_id != GF_M2TS_TABLE_ID_MPEG4_BIFS) && (table_id != GF_M2TS_TABLE_ID_MPEG4_OD) )
return;
/*skip if already received*/
if (status & GF_M2TS_TABLE_REPEAT)
if (!(es->flags & GF_M2TS_ES_SEND_REPEATED_SECTIONS))
return;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Sections for PID %d\n", es->pid) );
/*send all sections (eg SL-packets)*/
nb_sections = gf_list_count(sections);
for (i=0; i<nb_sections; i++) {
section = (GF_M2TS_Section *)gf_list_get(sections, i);
sl_pck.data = (char *)section->data;
sl_pck.data_len = section->data_size;
sl_pck.stream = (GF_M2TS_ES *)es;
sl_pck.version_number = version_number;
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck);
}
}
static void gf_m2ts_process_generic_section(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
GF_M2TS_GenericSectionInfo sinfo;
u32 i, nb_sec, evt_type;
if (!ts->on_event) return;
/*skip if already received*/
if (status & GF_M2TS_TABLE_REPEAT)
if (!(es->flags & GF_M2TS_ES_SEND_REPEATED_SECTIONS))
return;
evt_type = (status & GF_M2TS_TABLE_UPDATE) ? GF_M2TS_EVT_SECTION_UPDATE : GF_M2TS_EVT_SECTION;
memset(&sinfo, 0, sizeof(GF_M2TS_GenericSectionInfo));
sinfo.stream = (GF_M2TS_ES *) es;
sinfo.table_id = table_id;
sinfo.version_number = version_number;
sinfo.ex_table_id = ex_table_id;
sinfo.pts = es->program->last_pcr_value/300;
sinfo.num_sections = nb_sec = gf_list_count(sections);
for (i=0; i<nb_sec; i++) {
GF_M2TS_Section *section = (GF_M2TS_Section *)gf_list_get(sections, i);
sinfo.section_idx = i;
sinfo.section_data = section->data;
sinfo.section_data_len = section->data_size;
ts->on_event(ts, evt_type, &sinfo);
}
}
static void gf_m2ts_process_nit(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *nit_es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] NIT table processing (not yet implemented)"));
}
static void gf_m2ts_process_tdt_tot(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *tdt_tot_es, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
unsigned char *data;
u32 data_size, nb_sections;
u32 date, yp, mp, k;
GF_M2TS_Section *section;
GF_M2TS_TDT_TOT *time_table;
const char *table_name;
/*wait for the last section */
if ( !(status & GF_M2TS_TABLE_END) )
return;
switch (table_id) {
case GF_M2TS_TABLE_ID_TDT:
table_name = "TDT";
break;
case GF_M2TS_TABLE_ID_TOT:
table_name = "TOT";
break;
default:
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Unimplemented table_id %u for PID %u\n", table_id, GF_M2TS_PID_TDT_TOT_ST));
return;
}
nb_sections = gf_list_count(sections);
if (nb_sections > 1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] %s on multiple sections not supported\n", table_name));
}
section = (GF_M2TS_Section *)gf_list_get(sections, 0);
data = section->data;
data_size = section->data_size;
/*TOT only contains 40 bits of UTC_time; TDT add descriptors and a CRC*/
if ((table_id==GF_M2TS_TABLE_ID_TDT) && (data_size != 5)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Corrupted TDT size\n", table_name));
}
GF_SAFEALLOC(time_table, GF_M2TS_TDT_TOT);
if (!time_table) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Fail to alloc DVB time table\n"));
return;
}
if (data_size < 5) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Section data size too small to read date (len: %u)\n", data_size));
goto error_exit;
}
/*UTC_time - see annex C of DVB-SI ETSI EN 300468*/
/* decodes an Modified Julian Date (MJD) into a Co-ordinated Universal Time (UTC)
See annex C of DVB-SI ETSI EN 300468 */
date = data[0]*256 + data[1];
yp = GF_FLOAT_TO_U32((date - 15078.2)/365.25);
mp = GF_FLOAT_TO_U32((date - 14956.1 - (u32)(yp * 365.25))/30.6001);
time_table->day = GF_FLOAT_TO_U32(date - 14956 - (u32)(yp * 365.25) - (u32)(mp * 30.6001));
if (mp == 14 || mp == 15) k = 1;
else k = 0;
time_table->year = yp + k + 1900;
time_table->month = mp - 1 - k*12;
time_table->hour = 10*((data[2]&0xf0)>>4) + (data[2]&0x0f);
time_table->minute = 10*((data[3]&0xf0)>>4) + (data[3]&0x0f);
time_table->second = 10*((data[4]&0xf0)>>4) + (data[4]&0x0f);
if (!(time_table->hour<24 && time_table->minute<60 && time_table->second<60))
goto error_exit;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream UTC time is %u/%02u/%02u %02u:%02u:%02u\n", time_table->year, time_table->month, time_table->day, time_table->hour, time_table->minute, time_table->second));
switch (table_id) {
case GF_M2TS_TABLE_ID_TDT:
if (ts->TDT_time) gf_free(ts->TDT_time);
ts->TDT_time = time_table;
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_TDT, time_table);
break;
case GF_M2TS_TABLE_ID_TOT:
#if 0
{
u32 pos, loop_len;
loop_len = ((data[5]&0x0f) << 8) | (data[6] & 0xff);
data += 7;
pos = 0;
while (pos < loop_len) {
u8 tag = data[pos];
pos += 2;
if (tag == GF_M2TS_DVB_LOCAL_TIME_OFFSET_DESCRIPTOR) {
char tmp_time[10];
u16 offset_hours, offset_minutes;
now->country_code[0] = data[pos];
now->country_code[1] = data[pos+1];
now->country_code[2] = data[pos+2];
now->country_region_id = data[pos+3]>>2;
sprintf(tmp_time, "%02x", data[pos+4]);
offset_hours = atoi(tmp_time);
sprintf(tmp_time, "%02x", data[pos+5]);
offset_minutes = atoi(tmp_time);
now->local_time_offset_seconds = (offset_hours * 60 + offset_minutes) * 60;
if (data[pos+3] & 1) now->local_time_offset_seconds *= -1;
dvb_decode_mjd_to_unix_time(data+pos+6, &now->unix_next_toc);
sprintf(tmp_time, "%02x", data[pos+11]);
offset_hours = atoi(tmp_time);
sprintf(tmp_time, "%02x", data[pos+12]);
offset_minutes = atoi(tmp_time);
now->next_time_offset_seconds = (offset_hours * 60 + offset_minutes) * 60;
if (data[pos+3] & 1) now->next_time_offset_seconds *= -1;
pos+= 13;
}
}
/*TODO: check lengths are ok*/
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_TOT, time_table);
}
#endif
/*check CRC32*/
if (ts->tdt_tot->length<4) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted %s table (less than 4 bytes but CRC32 should be present\n", table_name));
goto error_exit;
}
if (!gf_m2ts_crc32_check(ts->tdt_tot->section, ts->tdt_tot->length-4)) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] corrupted %s table (CRC32 failed)\n", table_name));
goto error_exit;
}
if (ts->TDT_time) gf_free(ts->TDT_time);
ts->TDT_time = time_table;
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_TOT, time_table);
break;
default:
gf_assert(0);
goto error_exit;
}
return; /*success*/
error_exit:
gf_free(time_table);
return;
}
static GF_M2TS_MetadataPointerDescriptor *gf_m2ts_read_metadata_pointer_descriptor(GF_BitStream *bs, u32 length)
{
u32 size;
GF_M2TS_MetadataPointerDescriptor *d;
GF_SAFEALLOC(d, GF_M2TS_MetadataPointerDescriptor);
if (!d) return NULL;
d->application_format = gf_bs_read_u16(bs);
size = 2;
if (d->application_format == 0xFFFF) {
d->application_format_identifier = gf_bs_read_u32(bs);
size += 4;
}
d->format = gf_bs_read_u8(bs);
size += 1;
if (d->format == 0xFF) {
d->format_identifier = gf_bs_read_u32(bs);
size += 4;
}
d->service_id = gf_bs_read_u8(bs);
d->locator_record_flag = (gf_bs_read_int(bs, 1) ? GF_TRUE : GF_FALSE);
d->carriage_flag = (enum metadata_carriage)gf_bs_read_int(bs, 2);
gf_bs_read_int(bs, 5); /*reserved */
size += 2;
if (d->locator_record_flag) {
d->locator_length = gf_bs_read_u8(bs);
d->locator_data = (char *)gf_malloc(d->locator_length);
size += 1 + d->locator_length;
gf_bs_read_data(bs, d->locator_data, d->locator_length);
}
if (d->carriage_flag != 3) {
d->program_number = gf_bs_read_u16(bs);
size += 2;
}
if (d->carriage_flag == 1) {
d->ts_location = gf_bs_read_u16(bs);
d->ts_id = gf_bs_read_u16(bs);
size += 4;
}
if (length > size) {
d->data_size = length-size;
d->data = (char *)gf_malloc(d->data_size);
gf_bs_read_data(bs, d->data, d->data_size);
}
return d;
}
static void gf_m2ts_metadata_pointer_descriptor_del(GF_M2TS_MetadataPointerDescriptor *metapd)
{
if (metapd) {
if (metapd->locator_data) gf_free(metapd->locator_data);
if (metapd->data) gf_free(metapd->data);
gf_free(metapd);
}
}
static GF_M2TS_MetadataDescriptor *gf_m2ts_read_metadata_descriptor(GF_BitStream *bs, u32 length)
{
//u32 size;
GF_M2TS_MetadataDescriptor *d;
GF_SAFEALLOC(d, GF_M2TS_MetadataDescriptor);
if (!d) return NULL;
d->application_format = gf_bs_read_u16(bs);
//size = 2;
if (d->application_format == 0xFFFF) {
d->application_format_identifier = gf_bs_read_u32(bs);
//size += 4;
}
d->format = gf_bs_read_u8(bs);
//size += 1;
if (d->format == 0xFF) {
d->format_identifier = gf_bs_read_u32(bs);
//size += 4;
}
d->service_id = gf_bs_read_u8(bs);
d->decoder_config_flags = gf_bs_read_int(bs, 3);
d->dsmcc_flag = (gf_bs_read_int(bs, 1) ? GF_TRUE : GF_FALSE);
gf_bs_read_int(bs, 4); /* reserved */
//size += 2;
if (d->dsmcc_flag) {
d->service_id_record_length = gf_bs_read_u8(bs);
d->service_id_record = (char *)gf_malloc(d->service_id_record_length);
//size += 1 + d->service_id_record_length;
gf_bs_read_data(bs, d->service_id_record, d->service_id_record_length);
}
if (d->decoder_config_flags == 1) {
d->decoder_config_length = gf_bs_read_u8(bs);
d->decoder_config = (char *)gf_malloc(d->decoder_config_length);
//size += 1 + d->decoder_config_length;
gf_bs_read_data(bs, d->decoder_config, d->decoder_config_length);
}
if (d->decoder_config_flags == 3) {
d->decoder_config_id_length = gf_bs_read_u8(bs);
d->decoder_config_id = (char *)gf_malloc(d->decoder_config_id_length);
//size += 1 + d->decoder_config_id_length;
gf_bs_read_data(bs, d->decoder_config_id, d->decoder_config_id_length);
}
if (d->decoder_config_flags == 4) {
d->decoder_config_service_id = gf_bs_read_u8(bs);
//size++;
}
return d;
}
static void gf_m2ts_process_pmt(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *pmt, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
u32 info_length, pos, desc_len, evt_type, nb_es,i;
u32 nb_sections;
u32 data_size;
u32 nb_hevc_temp, nb_shvc, nb_shvc_temp, nb_mhvc, nb_mhvc_temp;
unsigned char *data;
GF_M2TS_Section *section;
GF_Err e = GF_OK;
//only process PMT tables
if (table_id != GF_M2TS_TABLE_ID_PMT) return;
/*wait for the last section */
if (!(status&GF_M2TS_TABLE_END)) return;
nb_es = 0;
/*skip if already received but no update detected (eg same data) */
if ((status&GF_M2TS_TABLE_REPEAT) && !(status&GF_M2TS_TABLE_UPDATE)) {
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
return;
}
if (pmt->sec->demux_restarted) {
pmt->sec->demux_restarted = 0;
return;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PMT Found or updated\n"));
nb_sections = gf_list_count(sections);
if (nb_sections > 1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PMT on multiple sections not supported\n"));
}
section = (GF_M2TS_Section *)gf_list_get(sections, 0);
data = section->data;
data_size = section->data_size;
if (data_size < 4) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT header data size %d\n", data_size ) );
return;
}
pmt->program->pcr_pid = ((data[0] & 0x1f) << 8) | data[1];
Bool is_scrambled = GF_FALSE;
info_length = ((data[2]&0xf)<<8) | data[3];
if (info_length + 4 > data_size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken PMT first loop, %d bytes avail but first loop size %d\n", data_size, info_length));
return;
} else if (info_length != 0) {
/* ...Read Descriptors ... */
u32 tag, len;
u32 first_loop_len = 0;
if (data_size < 6) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT header data size %d\n", data_size ) );
return;
}
tag = (u32) data[4];
len = (u32) data[5];
while (info_length > first_loop_len) {
if (tag == GF_M2TS_MPEG4_IOD_DESCRIPTOR) {
if ((len>2) && (len - 2 <= info_length) && (data_size>8) && (data_size-8 > (u32)len-2)) {
u32 size;
GF_BitStream *iod_bs;
iod_bs = gf_bs_new((char *)data+8, len-2, GF_BITSTREAM_READ);
if (pmt->program->pmt_iod) gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
pmt->program->pmt_iod = NULL;
e = gf_odf_parse_descriptor(iod_bs , (GF_Descriptor **) &pmt->program->pmt_iod, &size);
gf_bs_del(iod_bs );
if (pmt->program->pmt_iod && pmt->program->pmt_iod->tag != GF_ODF_IOD_TAG) {
GF_LOG( GF_LOG_ERROR, GF_LOG_CONTAINER, ("pmt iod has wrong tag %d\n", pmt->program->pmt_iod->tag) );
gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
pmt->program->pmt_iod = NULL;
}
if (e==GF_OK) {
/*remember program number for service/program selection*/
if (pmt->program->pmt_iod) {
pmt->program->pmt_iod->ServiceID = pmt->program->number;
/*if empty IOD (freebox case), discard it and use dynamic declaration of object*/
if (!gf_list_count(pmt->program->pmt_iod->ESDescriptors)) {
gf_odf_desc_del((GF_Descriptor *)pmt->program->pmt_iod);
pmt->program->pmt_iod = NULL;
}
}
}
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken IOD! len %d less than 2 bytes to declare IOD\n", len));
}
} else if (tag == GF_M2TS_METADATA_POINTER_DESCRIPTOR) {
GF_BitStream *metadatapd_bs;
GF_M2TS_MetadataPointerDescriptor *metapd;
if (data_size <= 8 || data_size-6 < (u32)len)
break;
metadatapd_bs = gf_bs_new((char *)data+6, len, GF_BITSTREAM_READ);
metapd = gf_m2ts_read_metadata_pointer_descriptor(metadatapd_bs, len);
gf_bs_del(metadatapd_bs);
if (metapd->application_format_identifier == GF_M2TS_META_ID3 &&
metapd->format_identifier == GF_M2TS_META_ID3 &&
metapd->carriage_flag == METADATA_CARRIAGE_SAME_TS) {
/*HLS ID3 Metadata */
if (pmt->program->metadata_pointer_descriptor)
gf_m2ts_metadata_pointer_descriptor_del(pmt->program->metadata_pointer_descriptor);
pmt->program->metadata_pointer_descriptor = metapd;
} else {
/* don't know what to do with it for now, delete */
gf_m2ts_metadata_pointer_descriptor_del(metapd);
}
} else if(tag == GF_M2TS_REGISTRATION_DESCRIPTOR && len >= 4 && data_size>9) {
u32 reg_desc_format = GF_4CC(data[6], data[7], data[8], data[9]);
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] Registration descriptor with format_identifier \"%s\"\n", gf_4cc_to_str(reg_desc_format)));
} else if (tag==GF_M2TS_CA_DESCRIPTOR) {
is_scrambled = GF_TRUE;
} else {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Skipping descriptor (0x%x) and others not supported\n", tag));
}
first_loop_len += 2 + len;
}
}
if (data_size <= 4 + info_length) return;
data += 4 + info_length;
data_size -= 4 + info_length;
pos = 0;
nb_hevc_temp = nb_shvc = nb_shvc_temp = nb_mhvc = nb_mhvc_temp = 0;
while (pos<data_size) {
GF_M2TS_PES *pes = NULL;
GF_M2TS_SECTION_ES *ses = NULL;
GF_M2TS_ES *es = NULL;
Bool inherit_pcr = 0;
Bool pmt_pid_reused = GF_FALSE;
u32 pid, stream_type, reg_desc_format;
if (pos + 5 > data_size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken PMT! size %d but position %d and need at least 5 bytes to declare es\n", data_size, pos));
break;
}
stream_type = data[0];
pid = ((data[1] & 0x1f) << 8) | data[2];
desc_len = ((data[3] & 0xf) << 8) | data[4];
if (!pid) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID 0 for es descriptor in PMT of program %d, reserved for PAT\n", pmt->pid) );
break;
}
if (pid==1) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID 1 for es descriptor in PMT of program %d, reserved for CAT\n", pmt->pid) );
break;
}
if (pid==pmt->pid) {
pmt_pid_reused = GF_TRUE;
} else {
u32 pcount = gf_list_count(ts->programs);
for(i=0; i<pcount; i++) {
GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs,i);
if(prog->pmt_pid == pid) {
pmt_pid_reused = GF_TRUE;
break;
}
}
}
if (pmt_pid_reused) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID %d for es descriptor in PMT of program %d, this PID is already assigned to a PMT\n", pid, pmt->pid) );
break;
}
if (desc_len > data_size-5) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT es descriptor size for PID %d\n", pid ) );
break;
}
if (!pid) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID 0 for es descriptor in PMT of program %d, reserved for PAT\n", pmt->pid) );
break;
}
if (pid==1) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID 1 for es descriptor in PMT of program %d, reserved for CAT\n", pmt->pid) );
break;
}
if (pid==pmt->pid) {
pmt_pid_reused = GF_TRUE;
} else {
u32 pcount = gf_list_count(ts->programs);
for(i=0; i<pcount; i++) {
GF_M2TS_Program *prog = (GF_M2TS_Program *)gf_list_get(ts->programs,i);
if(prog->pmt_pid == pid) {
pmt_pid_reused = GF_TRUE;
break;
}
}
}
if (pmt_pid_reused) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PID %d for es descriptor in PMT of program %d, this PID is already assigned to a PMT\n", pid, pmt->pid) );
break;
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("stream_type :%d \n",stream_type));
if ((stream_type==GF_M2TS_JPEG_XS) && gf_opts_get_bool("core", "m2ts-vvc-old"))
stream_type = GF_M2TS_VIDEO_VVC;
switch (stream_type) {
/* PES */
case GF_M2TS_VIDEO_MPEG1:
case GF_M2TS_VIDEO_MPEG2:
case GF_M2TS_VIDEO_DCII:
case GF_M2TS_VIDEO_MPEG4:
case GF_M2TS_SYSTEMS_MPEG4_PES:
case GF_M2TS_VIDEO_H264:
case GF_M2TS_VIDEO_SVC:
case GF_M2TS_VIDEO_MVCD:
case GF_M2TS_VIDEO_HEVC:
case GF_M2TS_VIDEO_HEVC_MCTS:
case GF_M2TS_VIDEO_HEVC_TEMPORAL:
case GF_M2TS_VIDEO_SHVC:
case GF_M2TS_VIDEO_SHVC_TEMPORAL:
case GF_M2TS_VIDEO_MHVC:
case GF_M2TS_VIDEO_MHVC_TEMPORAL:
case GF_M2TS_VIDEO_VVC:
case GF_M2TS_VIDEO_VVC_TEMPORAL:
case GF_M2TS_VIDEO_VC1:
case GF_M2TS_HLS_AVC_CRYPT:
inherit_pcr = 1;
case GF_M2TS_AUDIO_MPEG1:
case GF_M2TS_AUDIO_MPEG2:
case GF_M2TS_AUDIO_AAC:
case GF_M2TS_AUDIO_LATM_AAC:
case GF_M2TS_AUDIO_AC3:
case GF_M2TS_AUDIO_EC3:
case GF_M2TS_AUDIO_DTS:
case GF_M2TS_AUDIO_TRUEHD:
case GF_M2TS_AUDIO_OPUS:
case GF_M2TS_MHAS_MAIN:
case GF_M2TS_MHAS_AUX:
case GF_M2TS_SUBTITLE_DVB:
case GF_M2TS_METADATA_PES:
case GF_M2TS_HLS_AAC_CRYPT:
case GF_M2TS_HLS_AC3_CRYPT:
case GF_M2TS_HLS_EC3_CRYPT:
case GF_M2TS_VIDEO_AVS2:
case GF_M2TS_AUDIO_AVS2:
case GF_M2TS_VIDEO_AVS3:
case GF_M2TS_AUDIO_AVS3:
case 0xA1:
GF_SAFEALLOC(pes, GF_M2TS_PES);
if (!pes) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
return;
}
pes->cc = -1;
pes->flags = GF_M2TS_ES_IS_PES;
if (inherit_pcr)
pes->flags |= GF_M2TS_INHERIT_PCR;
es = (GF_M2TS_ES *)pes;
break;
case GF_M2TS_PRIVATE_DATA:
GF_SAFEALLOC(pes, GF_M2TS_PES);
if (!pes) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
return;
}
pes->cc = -1;
pes->flags = GF_M2TS_ES_IS_PES;
es = (GF_M2TS_ES *)pes;
break;
/* Sections */
case GF_M2TS_SYSTEMS_MPEG4_SECTIONS:
GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
if (!ses) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
return;
}
es = (GF_M2TS_ES *)ses;
es->flags |= GF_M2TS_ES_IS_SECTION;
/* carriage of ISO_IEC_14496 data in sections */
if (stream_type == GF_M2TS_SYSTEMS_MPEG4_SECTIONS) {
/*MPEG-4 sections need to be fully checked: if one section is lost, this means we lost
one SL packet in the AU so we must wait for the complete section again*/
ses->sec = gf_m2ts_section_filter_new(gf_m2ts_process_mpeg4section, 0);
/*create OD container*/
if (!pmt->program->additional_ods) {
pmt->program->additional_ods = gf_list_new();
ts->has_4on2 = 1;
}
}
break;
case GF_M2TS_13818_6_ANNEX_A:
case GF_M2TS_13818_6_ANNEX_B:
case GF_M2TS_13818_6_ANNEX_C:
case GF_M2TS_13818_6_ANNEX_D:
case GF_M2TS_PRIVATE_SECTION:
case GF_M2TS_QUALITY_SEC:
case GF_M2TS_MORE_SEC:
case GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS:
GF_SAFEALLOC(ses, GF_M2TS_SECTION_ES);
if (!ses) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG2TS] Failed to allocate ES for pid %d\n", pid));
return;
}
es = (GF_M2TS_ES *)ses;
es->flags |= GF_M2TS_ES_IS_SECTION;
es->pid = pid;
es->service_id = pmt->program->number;
if (stream_type == GF_M2TS_PRIVATE_SECTION) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("AIT sections on pid %d\n", pid));
} else if (stream_type == GF_M2TS_QUALITY_SEC) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Quality metadata sections on pid %d\n", pid));
} else if (stream_type == GF_M2TS_MORE_SEC) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("MORE sections on pid %d\n", pid));
} else if (stream_type == GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("SCTE35 Splice Info sections on pid %d\n", pid));
} else {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type DSM CC user private sections on pid %d \n", pid));
}
/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
ses->sec = gf_m2ts_section_filter_new(NULL, 1);
//ses->sec->service_id = pmt->program->number;
break;
case GF_M2TS_MPE_SECTIONS:
if (! ts->prefix_present) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("stream type MPE found : pid = %d \n", pid));
#ifdef GPAC_ENABLE_MPE
es = gf_dvb_mpe_section_new();
if (es->flags & GF_M2TS_ES_IS_SECTION) {
/* NULL means: trigger the call to on_event with DVB_GENERAL type and the raw section as payload */
((GF_M2TS_SECTION_ES*)es)->sec = gf_m2ts_section_filter_new(NULL, 1);
}
#endif
break;
}
default:
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
break;
}
if (es) {
es->stream_type = (stream_type==GF_M2TS_PRIVATE_DATA) ? 0 : stream_type;
es->program = pmt->program;
es->pid = pid;
es->component_tag = -1;
}
pos += 5;
data += 5;
while (desc_len) {
if (pos + 2 > data_size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken PMT descriptor! size %d but position %d and need at least 2 bytes to parse descriptor\n", data_size, pos));
break;
}
u8 tag = data[0];
u32 len = data[1];
if (pos + 2 + len > data_size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken PMT descriptor! size %d, desc size %d but position %d\n", data_size, len, pos));
break;
}
if (es) {
switch (tag) {
case GF_M2TS_ISO_639_LANGUAGE_DESCRIPTOR:
if (pes && (len>=3) )
pes->lang = GF_4CC(' ', data[2], data[3], data[4]);
break;
case GF_M2TS_MPEG4_SL_DESCRIPTOR:
if (len>=2) {
es->mpeg4_es_id = ( (u32) data[2] & 0x1f) << 8 | data[3];
es->flags |= GF_M2TS_ES_IS_SL;
}
break;
case GF_M2TS_REGISTRATION_DESCRIPTOR:
if (len>=4) {
reg_desc_format = GF_4CC(data[2], data[3], data[4], data[5]);
/* cf https://smpte-ra.org/registered-mpeg-ts-ids */
switch (reg_desc_format) {
case GF_M2TS_RA_STREAM_AC3:
//don't overwrite if already EAC3 or TrueHD
if ((es->stream_type != GF_M2TS_AUDIO_EC3) && (es->stream_type != GF_M2TS_AUDIO_TRUEHD))
es->stream_type = GF_M2TS_AUDIO_AC3;
break;
case GF_M2TS_RA_STREAM_EAC3:
//don't overwrite if already AC3 or TrueHD
if ((es->stream_type != GF_M2TS_AUDIO_AC3) && (es->stream_type != GF_M2TS_AUDIO_TRUEHD))
es->stream_type = GF_M2TS_AUDIO_EC3;
break;
case GF_M2TS_RA_STREAM_AC4:
es->stream_type = GF_M2TS_AUDIO_AC4;
break;
case GF_M2TS_RA_STREAM_VC1:
es->stream_type = GF_M2TS_VIDEO_VC1;
break;
case GF_M2TS_RA_STREAM_HEVC:
es->stream_type = GF_M2TS_VIDEO_HEVC;
break;
case GF_M2TS_RA_STREAM_DTS1:
case GF_M2TS_RA_STREAM_DTS2:
case GF_M2TS_RA_STREAM_DTS3:
es->stream_type = GF_M2TS_AUDIO_DTS;
break;
case GF_M2TS_RA_STREAM_AVSA:
es->stream_type = GF_M2TS_AUDIO_AVS3;
break;
case GF_M2TS_RA_STREAM_AVSV:
es->stream_type = GF_M2TS_VIDEO_AVS3;
// AVS3-P6-TAI 109.6 Table-9
if (len<10) break;
memcpy(pes->avs3_video_descriptor, data+6, 10);
break;
case GF_M2TS_RA_STREAM_OPUS:
es->stream_type = GF_M2TS_AUDIO_OPUS;
break;
case GF_M2TS_RA_STREAM_DOVI:
break;
case GF_M2TS_RA_STREAM_AV1:
es->stream_type = GF_M2TS_VIDEO_AV1;
break;
case GF_M2TS_RA_STREAM_SCTE35:
es->stream_type = GF_M2TS_SCTE35_SPLICE_INFO_SECTIONS;
break;
case GF_M2TS_RA_STREAM_SRT:
es->stream_type = GF_M2TS_METADATA_SRT;
break;
case GF_M2TS_RA_STREAM_TXT:
es->stream_type = GF_M2TS_METADATA_TEXT;
break;
case GF_M2TS_RA_STREAM_GPAC:
if (len<8) break;
es->stream_type = GF_4CC(data[6], data[7], data[8], data[9]);
es->flags |= GF_M2TS_GPAC_CODEC_ID;
if ((len>12) && (es->flags & GF_M2TS_ES_IS_PES)) {
pes = (GF_M2TS_PES*)es;
pes->gpac_meta_dsi_size = len-4;
pes->gpac_meta_dsi = gf_realloc(pes->gpac_meta_dsi, pes->gpac_meta_dsi_size);
if (pes->gpac_meta_dsi)
memcpy(pes->gpac_meta_dsi, data+6, pes->gpac_meta_dsi_size);
}
break;
default:
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("Unknown registration descriptor %s\n", gf_4cc_to_str(reg_desc_format) ));
break;
}
}
break;
case GF_M2TS_DVB_EAC3_DESCRIPTOR:
es->stream_type = GF_M2TS_AUDIO_EC3;
break;
case GF_M2TS_DVB_DATA_BROADCAST_ID_DESCRIPTOR:
if (len>=2) {
u32 id = data[2]<<8 | data[3];
if ((id == 0xB) && ses && !ses->sec) {
ses->sec = gf_m2ts_section_filter_new(NULL, 1);
}
}
break;
case GF_M2TS_DVB_SUBTITLING_DESCRIPTOR:
if (pes) {
if (len>=8) {
pes->sub.language[0] = data[2];
pes->sub.language[1] = data[3];
pes->sub.language[2] = data[4];
pes->sub.type = data[5];
pes->sub.composition_page_id = (data[6]<<8) | data[7];
pes->sub.ancillary_page_id = (data[8]<<8) | data[9];
} else {
memset(& (pes->sub), 0, sizeof(pes->sub));
}
es->stream_type = GF_M2TS_DVB_SUBTITLE;
}
break;
case GF_M2TS_DVB_STREAM_IDENTIFIER_DESCRIPTOR:
if (len>=1) {
es->component_tag = data[2];
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("Component Tag: %d on Program %d PID %d\n", es->component_tag, es->program->number, es->pid));
}
break;
case GF_M2TS_DVB_TELETEXT_DESCRIPTOR:
es->stream_type = GF_M2TS_DVB_TELETEXT;
break;
case GF_M2TS_DVB_VBI_DATA_DESCRIPTOR:
es->stream_type = GF_M2TS_DVB_VBI;
break;
case GF_M2TS_HIERARCHY_DESCRIPTOR:
if (pes && (len>=4)) {
u8 hierarchy_embedded_layer_index;
GF_BitStream *hbs = gf_bs_new((const char *)data, data_size, GF_BITSTREAM_READ);
/*u32 skip = */gf_bs_read_int(hbs, 16);
/*u8 res1 = */gf_bs_read_int(hbs, 1);
/*u8 temp_scal = */gf_bs_read_int(hbs, 1);
/*u8 spatial_scal = */gf_bs_read_int(hbs, 1);
/*u8 quality_scal = */gf_bs_read_int(hbs, 1);
/*u8 hierarchy_type = */gf_bs_read_int(hbs, 4);
/*u8 res2 = */gf_bs_read_int(hbs, 2);
/*u8 hierarchy_layer_index = */gf_bs_read_int(hbs, 6);
/*u8 tref_not_present = */gf_bs_read_int(hbs, 1);
/*u8 res3 = */gf_bs_read_int(hbs, 1);
hierarchy_embedded_layer_index = gf_bs_read_int(hbs, 6);
/*u8 res4 = */gf_bs_read_int(hbs, 2);
/*u8 hierarchy_channel = */gf_bs_read_int(hbs, 6);
gf_bs_del(hbs);
pes->depends_on_pid = 1+hierarchy_embedded_layer_index;
}
break;
case GF_M2TS_METADATA_DESCRIPTOR:
{
GF_BitStream *metadatad_bs;
GF_M2TS_MetadataDescriptor *metad;
metadatad_bs = gf_bs_new((char *)data+2, len, GF_BITSTREAM_READ);
metad = gf_m2ts_read_metadata_descriptor(metadatad_bs, len);
gf_bs_del(metadatad_bs);
if (metad->application_format_identifier == GF_M2TS_META_ID3 &&
metad->format_identifier == GF_M2TS_META_ID3) {
/*HLS ID3 Metadata*/
if (pes) {
if (pes->metadata_descriptor)
gf_m2ts_metadata_descriptor_del(pes->metadata_descriptor);
pes->metadata_descriptor = metad;
pes->stream_type = GF_M2TS_METADATA_ID3_HLS;
}
else {
gf_m2ts_metadata_descriptor_del(metad);
}
} else if (metad->format_identifier == GF_M2TS_META_KLVA) {
/*ID3 with KLVA generic encoding (https://en.wikipedia.org/wiki/KLV)*/
if (pes) {
if (pes->metadata_descriptor)
gf_m2ts_metadata_descriptor_del(pes->metadata_descriptor);
pes->metadata_descriptor = metad;
pes->stream_type = GF_M2TS_METADATA_ID3_KLVA;
}
else {
gf_m2ts_metadata_descriptor_del(metad);
}
} else {
/* don't know what to do with it for now, delete */
gf_m2ts_metadata_descriptor_del(metad);
}
}
break;
case GF_M2TS_HEVC_VIDEO_DESCRIPTOR:
if (es) es->stream_type = GF_M2TS_VIDEO_HEVC;
break;
case GF_M2TS_AVC_VIDEO_DESCRIPTOR:
if (es) es->stream_type = GF_M2TS_VIDEO_H264;
break;
case GF_M2TS_DOLBY_VISION_DESCRIPTOR:
if (pes && (len>=5)) {
GF_BitStream *hbs = gf_bs_new((const char *)data+2, len, GF_BITSTREAM_READ);
pes->dv_info[0] = gf_bs_read_u8(hbs);
pes->dv_info[1] = gf_bs_read_u8(hbs);
pes->dv_info[2] = gf_bs_read_u8(hbs);
pes->dv_info[3] = gf_bs_read_u8(hbs);
if (! (pes->dv_info[3] & 0x1) ) {
pes->depends_on_pid = gf_bs_read_int(hbs, 13);
gf_bs_read_int(hbs, 3);
}
pes->dv_info[4] = gf_bs_read_u8(hbs);
gf_bs_del(hbs);
}
break;
case GF_M2TS_MAX_BITRATE_DESCRIPTOR:
break;
case GF_M2TS_DVB_EXT_DESCRIPTOR:
if ((len>4) && (data[2] == 0x06) && pes) {
u32 flags = data[3];
u32 aflags = (flags >> 2) & 0x1F;
if ((flags & 0x80) == 0)
pes->audio_flags |= GF_M2TS_AUDIO_SUBSTREAM_COMP;
switch (aflags) {
case 0x01:
pes->audio_flags |= GF_M2TS_AUDIO_DESCRIPTION;
break;
case 0x02:
pes->audio_flags |= GF_M2TS_AUDIO_HEARING_IMPAIRED;
break;
case 0x03:
pes->audio_flags |= GF_M2TS_AUDIO_SUB_DESCRIPTION;
break;
}
if ((flags & 0x01) && (len>=7)) {
pes->lang = ' ';
pes->lang <<= 8;
pes->lang = data[4];
pes->lang <<= 8;
pes->lang = data[5];
pes->lang <<= 8;
pes->lang = data[6];
}
}
break;
default:
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] skipping descriptor (0x%x) not supported\n", tag));
break;
}
}
data += len+2;
pos += len+2;
if (desc_len < len+2) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Invalid PMT es descriptor size for PID %d\n", pid ) );
break;
}
desc_len-=len+2;
}
if (es && !es->stream_type) {
#if 0
gf_free(es);
es = NULL;
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Private Stream type (0x%x) for PID %d not supported\n", stream_type, pid ) );
#else
es->stream_type = stream_type;
#endif
}
if (!es) continue;
if (pes && (stream_type==GF_M2TS_PRIVATE_DATA) && (es->stream_type!=stream_type) && pes->dv_info[0]) {
//non-compatible base layer dolby vision
pes->dv_info[24] = 1;
}
if (pes) pes->is_protected = is_scrambled;
if (ts->ess[pid]) {
//this is component reuse across programs, overwrite the previously declared stream ...
if (status & GF_M2TS_TABLE_FOUND) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d reused across programs %d and %d, not completely supported\n", pid, ts->ess[pid]->program->number, es->program->number ) );
//add stream to program but don't reassign the pid table until the stream is playing (>GF_M2TS_PES_FRAMING_SKIP)
gf_list_add(pmt->program->streams, es);
if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);
nb_es++;
//skip assignment below
es = NULL;
}
/*watchout for pmt update - FIXME this likely won't work in most cases*/
else {
GF_M2TS_ES *o_es = ts->ess[es->pid];
if ((o_es->stream_type == es->stream_type)
&& ((o_es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK) == (es->flags & GF_M2TS_ES_STATIC_FLAGS_MASK))
&& (o_es->mpeg4_es_id == es->mpeg4_es_id)
&& ((o_es->flags & GF_M2TS_ES_IS_SECTION) || ((GF_M2TS_PES *)o_es)->lang == ((GF_M2TS_PES *)es)->lang)
) {
gf_m2ts_es_del(es, ts);
es = NULL;
} else {
gf_m2ts_es_del(o_es, ts);
ts->ess[es->pid] = NULL;
}
}
}
if (es) {
ts->ess[es->pid] = es;
gf_list_add(pmt->program->streams, es);
if (!(es->flags & GF_M2TS_ES_IS_SECTION) ) gf_m2ts_set_pes_framing(pes, GF_M2TS_PES_FRAMING_SKIP);
nb_es++;
if (es->stream_type == GF_M2TS_VIDEO_HEVC_TEMPORAL) nb_hevc_temp++;
else if (es->stream_type == GF_M2TS_VIDEO_SHVC) nb_shvc++;
else if (es->stream_type == GF_M2TS_VIDEO_SHVC_TEMPORAL) nb_shvc_temp++;
else if (es->stream_type == GF_M2TS_VIDEO_MHVC) nb_mhvc++;
else if (es->stream_type == GF_M2TS_VIDEO_MHVC_TEMPORAL) nb_mhvc_temp++;
}
}
//Table 2-139, implied hierarchy indexes
if (nb_hevc_temp + nb_shvc + nb_shvc_temp + nb_mhvc + nb_mhvc_temp) {
for (i=0; i<gf_list_count(pmt->program->streams); i++) {
GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
if (es->depends_on_pid) continue;
switch (es->stream_type) {
case GF_M2TS_VIDEO_HEVC_TEMPORAL:
es->depends_on_pid = 1;
break;
case GF_M2TS_VIDEO_SHVC:
if (!nb_hevc_temp) es->depends_on_pid = 1;
else es->depends_on_pid = 2;
break;
case GF_M2TS_VIDEO_SHVC_TEMPORAL:
es->depends_on_pid = 3;
break;
case GF_M2TS_VIDEO_MHVC:
if (!nb_hevc_temp) es->depends_on_pid = 1;
else es->depends_on_pid = 2;
break;
case GF_M2TS_VIDEO_MHVC_TEMPORAL:
if (!nb_hevc_temp) es->depends_on_pid = 2;
else es->depends_on_pid = 3;
break;
}
}
}
if (nb_es) {
//translate hierarchy descriptors indexes into PIDs - check whether the PMT-index rules are the same for HEVC
for (i=0; i<gf_list_count(pmt->program->streams); i++) {
GF_M2TS_PES *an_es = NULL;
GF_M2TS_PES *es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, i);
if ( !(es->flags & GF_M2TS_ES_IS_PES)) continue;
if (!es->depends_on_pid) continue;
//fixeme we are not always assured that hierarchy_layer_index matches the stream index...
//+1 is because our first stream is the PMT
an_es = (GF_M2TS_PES *)gf_list_get(pmt->program->streams, es->depends_on_pid);
if (an_es) {
es->depends_on_pid = an_es->pid;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS] Wrong dependency index in hierarchy descriptor, assuming non-scalable stream\n"));
es->depends_on_pid = 0;
}
}
evt_type = (status&GF_M2TS_TABLE_FOUND) ? GF_M2TS_EVT_PMT_FOUND : GF_M2TS_EVT_PMT_UPDATE;
if (ts->on_event) ts->on_event(ts, evt_type, pmt->program);
} else {
/* if we found no new ES it's simply a repeat of the PMT */
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PMT_REPEAT, pmt->program);
}
}
static void gf_m2ts_process_pat(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
GF_M2TS_Program *prog;
GF_M2TS_SECTION_ES *pmt;
u32 i, nb_progs, evt_type;
u32 nb_sections;
u32 data_size;
unsigned char *data;
GF_M2TS_Section *section;
//only process PAT tables
if (table_id != GF_M2TS_TABLE_ID_PAT) return;
/*wait for the last section */
if (!(status&GF_M2TS_TABLE_END)) return;
/*skip if already received*/
if (status&GF_M2TS_TABLE_REPEAT) {
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PAT_REPEAT, NULL);
return;
}
nb_sections = gf_list_count(sections);
if (nb_sections > 1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PAT on multiple sections not supported\n"));
}
section = (GF_M2TS_Section *)gf_list_get(sections, 0);
data = section->data;
data_size = section->data_size;
if (!(status&GF_M2TS_TABLE_UPDATE) && gf_list_count(ts->programs)) {
if (ts->pat->demux_restarted) {
ts->pat->demux_restarted = 0;
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Multiple different PAT on single TS found, ignoring new PAT declaration (table id %d - extended table id %d)\n", table_id, ex_table_id));
}
return;
}
nb_progs = data_size / 4;
for (i=0; i<nb_progs; i++) {
u16 number, pid;
number = (data[0]<<8) | data[1];
pid = (data[2]&0x1f)<<8 | data[3];
data += 4;
if (number==0) {
if (!ts->nit) {
ts->nit = gf_m2ts_section_filter_new(gf_m2ts_process_nit, 0);
}
} else if (!pid) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Broken PAT found reserved PID 0, ignoring\n", pid));
} else if (! ts->ess[pid]) {
GF_SAFEALLOC(prog, GF_M2TS_Program);
if (!prog) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Fail to allocate program for pid %d\n", pid));
return;
}
prog->streams = gf_list_new();
prog->pmt_pid = pid;
prog->number = number;
prog->ts = ts;
gf_list_add(ts->programs, prog);
GF_SAFEALLOC(pmt, GF_M2TS_SECTION_ES);
if (!pmt) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Fail to allocate pmt filter for pid %d\n", pid));
return;
}
pmt->flags = GF_M2TS_ES_IS_SECTION | GF_M2TS_ES_IS_PMT;
gf_list_add(prog->streams, pmt);
pmt->pid = prog->pmt_pid;
pmt->program = prog;
ts->ess[pmt->pid] = (GF_M2TS_ES *)pmt;
pmt->sec = gf_m2ts_section_filter_new(gf_m2ts_process_pmt, 0);
}
}
evt_type = (status&GF_M2TS_TABLE_UPDATE) ? GF_M2TS_EVT_PAT_UPDATE : GF_M2TS_EVT_PAT_FOUND;
if (ts->on_event) {
GF_M2TS_SectionInfo sinfo;
sinfo.ex_table_id = ex_table_id;
sinfo.table_id = table_id;
sinfo.version_number = version_number;
ts->on_event(ts, evt_type, &sinfo);
}
}
static void gf_m2ts_process_cat(GF_M2TS_Demuxer *ts, GF_M2TS_SECTION_ES *ses, GF_List *sections, u8 table_id, u16 ex_table_id, u8 version_number, u8 last_section_number, u32 status)
{
u32 evt_type;
/*
GF_M2TS_Program *prog;
GF_M2TS_SECTION_ES *pmt;
u32 i, nb_progs;
u32 nb_sections;
u32 data_size;
unsigned char *data;
GF_M2TS_Section *section;
*/
//only process CAT tables
if (table_id != GF_M2TS_TABLE_ID_CAT) return;
/*wait for the last section */
if (!(status&GF_M2TS_TABLE_END)) return;
/*skip if already received*/
if (status&GF_M2TS_TABLE_REPEAT) {
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_CAT_REPEAT, NULL);
return;
}
/*
nb_sections = gf_list_count(sections);
if (nb_sections > 1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("CAT on multiple sections not supported\n"));
}
section = (GF_M2TS_Section *)gf_list_get(sections, 0);
data = section->data;
data_size = section->data_size;
nb_progs = data_size / 4;
for (i=0; i<nb_progs; i++) {
u16 number, pid;
number = (data[0]<<8) | data[1];
pid = (data[2]&0x1f)<<8 | data[3];
data += 4;
if (number==0) {
if (!ts->nit) {
ts->nit = gf_m2ts_section_filter_new(gf_m2ts_process_nit, 0);
}
} else {
GF_SAFEALLOC(prog, GF_M2TS_Program);
prog->streams = gf_list_new();
prog->pmt_pid = pid;
prog->number = number;
gf_list_add(ts->programs, prog);
GF_SAFEALLOC(pmt, GF_M2TS_SECTION_ES);
pmt->flags = GF_M2TS_ES_IS_SECTION;
gf_list_add(prog->streams, pmt);
pmt->pid = prog->pmt_pid;
pmt->program = prog;
ts->ess[pmt->pid] = (GF_M2TS_ES *)pmt;
pmt->sec = gf_m2ts_section_filter_new(gf_m2ts_process_pmt, 0);
}
}
*/
evt_type = (status&GF_M2TS_TABLE_UPDATE) ? GF_M2TS_EVT_CAT_UPDATE : GF_M2TS_EVT_CAT_FOUND;
if (ts->on_event) ts->on_event(ts, evt_type, NULL);
}
u64 gf_m2ts_get_pts(unsigned char *data)
{
u64 pts;
u32 val;
pts = (u64)((data[0] >> 1) & 0x07) << 30;
val = (data[1] << 8) | data[2];
pts |= (u64)(val >> 1) << 15;
val = (data[3] << 8) | data[4];
pts |= (u64)(val >> 1);
return pts;
}
void gf_m2ts_pes_header(GF_M2TS_PES *pes, unsigned char *data, u32 data_size, GF_M2TS_PESHeader *pesh)
{
u32 has_pts, has_dts;
u32 len_check;
memset(pesh, 0, sizeof(GF_M2TS_PESHeader));
if (data_size < 6) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("PES Header is too small (%d < 6)\n", data_size));
return;
}
len_check = 0;
pesh->id = data[0];
pesh->pck_len = (data[1]<<8) | data[2];
/*
2bits
scrambling_control = gf_bs_read_int(bs,2);
priority = gf_bs_read_int(bs,1);
*/
pesh->data_alignment = (data[3] & 0x4) ? 1 : 0;
/*
copyright = gf_bs_read_int(bs,1);
original = gf_bs_read_int(bs,1);
*/
has_pts = (data[4]&0x80);
has_dts = has_pts ? (data[4]&0x40) : 0;
/*
ESCR_flag = gf_bs_read_int(bs,1);
ES_rate_flag = gf_bs_read_int(bs,1);
DSM_flag = gf_bs_read_int(bs,1);
additional_copy_flag = gf_bs_read_int(bs,1);
prev_crc_flag = gf_bs_read_int(bs,1);
extension_flag = gf_bs_read_int(bs,1);
*/
pesh->hdr_data_len = data[5];
data += 6;
if (has_pts) {
pesh->PTS = gf_m2ts_get_pts(data);
data+=5;
len_check += 5;
pesh->has_pts = 1;
}
if (has_dts) {
pesh->DTS = gf_m2ts_get_pts(data);
//data+=5;
len_check += 5;
} else {
pesh->DTS = pesh->PTS;
}
if (len_check < pesh->hdr_data_len) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Skipping %d bytes in pes header\n", pes->pid, pesh->hdr_data_len - len_check));
} else if (len_check > pesh->hdr_data_len) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Wrong pes_header_data_length field %d bytes - read %d\n", pes->pid, pesh->hdr_data_len, len_check));
}
if ((pesh->PTS<90000) && ((s32)pesh->DTS<0) && (GF_M2TS_MAX_PCR_90K - pesh->DTS >= 18000)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Wrong DTS %d negative for PTS %d - forcing to 0\n", pes->pid, pesh->DTS, pesh->PTS));
pesh->DTS=0;
}
}
static void gf_m2ts_store_temi(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes)
{
GF_BitStream *bs = gf_bs_new(pes->temi_tc_desc, pes->temi_tc_desc_len, GF_BITSTREAM_READ);
u32 has_timestamp = gf_bs_read_int(bs, 2);
Bool has_ntp = (Bool) gf_bs_read_int(bs, 1);
/*u32 has_ptp = */gf_bs_read_int(bs, 1);
/*u32 has_timecode = */gf_bs_read_int(bs, 2);
memset(&pes->temi_tc, 0, sizeof(GF_M2TS_TemiTimecodeDescriptor));
pes->temi_tc.force_reload = gf_bs_read_int(bs, 1);
pes->temi_tc.is_paused = gf_bs_read_int(bs, 1);
pes->temi_tc.is_discontinuity = gf_bs_read_int(bs, 1);
gf_bs_read_int(bs, 7);
pes->temi_tc.timeline_id = gf_bs_read_int(bs, 8);
if (has_timestamp) {
pes->temi_tc.media_timescale = gf_bs_read_u32(bs);
if (has_timestamp==2)
pes->temi_tc.media_timestamp = gf_bs_read_u64(bs);
else
pes->temi_tc.media_timestamp = gf_bs_read_u32(bs);
}
if (has_ntp) {
pes->temi_tc.ntp = gf_bs_read_u64(bs);
}
gf_bs_del(bs);
pes->temi_tc_desc_len = 0;
pes->temi_pending = GF_TRUE;
}
void gf_m2ts_flush_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, u32 force_flush_type)
{
GF_M2TS_PESHeader pesh;
if (!ts) return;
/*we need at least a full, valid start code and PES header !!*/
if ((pes->pck_data_len >= 4) && !pes->pck_data[0] && !pes->pck_data[1] && (pes->pck_data[2] == 0x1)) {
u32 len;
Bool has_pes_header = GF_TRUE;
Bool has_data = GF_TRUE;
u32 stream_id = pes->pck_data[3];
Bool same_pts = GF_FALSE;
switch (stream_id) {
case GF_M2_STREAMID_PADDING:
has_data = GF_FALSE;
case GF_M2_STREAMID_PROGRAM_STREAM_MAP:
case GF_M2_STREAMID_PRIVATE_2:
case GF_M2_STREAMID_ECM:
case GF_M2_STREAMID_EMM:
case GF_M2_STREAMID_PROGRAM_STREAM_DIRECTORY:
case GF_M2_STREAMID_DSMCC:
case GF_M2_STREAMID_H222_TYPE_E:
has_pes_header = GF_FALSE;
break;
}
if (has_pes_header) {
/*OK read header*/
gf_m2ts_pes_header(pes, pes->pck_data + 3, pes->pck_data_len - 3, &pesh);
/*send PES timing*/
if (ts->notify_pes_timing && pesh.has_pts) {
GF_M2TS_PES_PCK pck;
memset(&pck, 0, sizeof(GF_M2TS_PES_PCK));
pck.PTS = pesh.PTS;
pck.DTS = pesh.DTS;
pck.stream = pes;
if (pes->rap) pck.flags |= GF_M2TS_PES_PCK_RAP;
pes->pes_end_packet_number = ts->pck_number;
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_PES_TIMING, &pck);
}
if (pesh.has_pts) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Got PES header DTS %u PTS %u\n", pes->pid, pesh.DTS, pesh.PTS));
if (pes->before_last_pes_start_pn && (pesh.PTS == pes->PTS)) {
same_pts = GF_TRUE;
if ((pes->stream_type==GF_M2TS_AUDIO_TRUEHD) || (pes->stream_type==GF_M2TS_AUDIO_EC3)) {
same_pts = GF_FALSE;
} else if (!pes->is_resume) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - same PTS "LLU" for two consecutive PES packets \n", pes->pid, pes->PTS));
}
}
#ifndef GPAC_DISABLE_LOG
/*FIXME - this test should only be done for non bi-directionally coded media
else if (pesh.PTS < pes->PTS) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - PTS "LLU" less than previous packet PTS "LLU"\n", pes->pid, pesh.PTS, pes->PTS) );
}
*/
#endif
pes->PTS = pesh.PTS;
#ifndef GPAC_DISABLE_LOG
if (!pes->is_resume && pes->before_last_pes_start_pn && (pesh.DTS == pes->DTS)) {
if ((pes->stream_type==GF_M2TS_AUDIO_TRUEHD) || (pes->stream_type==GF_M2TS_AUDIO_EC3)) {
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - same DTS "LLU" for two consecutive PES packets \n", pes->pid, pes->DTS));
}
}
if (pesh.DTS < pes->DTS) {
if (GF_M2TS_MAX_PCR_90K + pesh.DTS < pes->DTS) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d - DTS "LLU" less than previous DTS "LLU"\n", pes->pid, pesh.DTS, pes->DTS));
}
}
#endif
pes->DTS = pesh.DTS;
}
/*no PTSs were coded, same time*/
else {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d Got PES header no timestamps\n", pes->pid));
if (!pesh.hdr_data_len)
same_pts = GF_TRUE;
}
pes->is_resume = GF_FALSE;
/*3-byte start-code + 6 bytes header + hdr extensions*/
len = 9 + pesh.hdr_data_len;
} else {
if (!has_data) goto exit;
/*3-byte start-code + 1 byte streamid*/
len = 4;
memset(&pesh, 0, sizeof(pesh));
}
if ((u8) pes->pck_data[3]==0xfa) {
GF_M2TS_SL_PCK sl_pck;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] SL Packet in PES for %d - ES ID %d\n", pes->pid, pes->mpeg4_es_id));
if (pes->pck_data_len > len) {
sl_pck.data = (char *)pes->pck_data + len;
sl_pck.data_len = pes->pck_data_len - len;
sl_pck.stream = (GF_M2TS_ES *)pes;
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_SL_PCK, &sl_pck);
} else {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] Bad SL Packet size: (%d indicated < %d header)\n", pes->pid, pes->pck_data_len, len));
}
goto exit;
}
if (pes->reframe) {
u32 remain = 0;
u32 offset = len;
if (pesh.pck_len && (pesh.pck_len-3-pesh.hdr_data_len != pes->pck_data_len-len)) {
if (!force_flush_type) {
pes->is_resume = GF_TRUE;
return;
}
if (force_flush_type==1) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PES payload size %d but received %d bytes\n", pes->pid, (u32) ( pesh.pck_len-3-pesh.hdr_data_len), pes->pck_data_len-len));
}
}
//copy over the remaining of previous PES payload before start of this PES payload
if (pes->prev_data_len) {
if (pes->prev_data_len < len) {
offset = len - pes->prev_data_len;
memcpy(pes->pck_data + offset, pes->prev_data, pes->prev_data_len);
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PES reassembly buffer overflow (%d bytes not processed from previous PES) - discarding prev data\n", pes->pid, pes->prev_data_len ));
}
}
if (pes->temi_tc_desc_len && !pes->temi_pending) {
gf_m2ts_store_temi(ts, pes);
}
if (pes->temi_pending) {
pes->temi_pending = GF_FALSE;
pes->temi_tc.pes_pts = pes->PTS;
pes->temi_tc.pid = pes->pid;
if (ts->on_event)
ts->on_event(ts, GF_M2TS_EVT_TEMI_TIMECODE, &pes->temi_tc);
}
if (!ts->seek_mode && pes->pck_data_len > offset) {
remain = pes->reframe(ts, pes, same_pts, pes->pck_data+offset, pes->pck_data_len-offset, &pesh);
}
//CLEANUP alloc stuff
if (pes->prev_data) gf_free(pes->prev_data);
pes->prev_data = NULL;
pes->prev_data_len = 0;
if (remain) {
pes->prev_data = gf_malloc(sizeof(char)*remain);
if (pes->pck_data_len >= remain)
memcpy(pes->prev_data, pes->pck_data + pes->pck_data_len - remain, remain);
pes->prev_data_len = remain;
}
}
} else if (pes->pck_data_len < 4) {
if (pes->pck_data_len) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d invalid PES header size %u\n", pes->pid, pes->pck_data_len));
}
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d invalid PES startcode %02x%02x%02x\n", pes->pid, pes->pck_data[0], pes->pck_data[1], pes->pck_data[2]));
}
exit:
pes->pck_data_len = 0;
pes->pes_len = 0;
pes->rap = 0;
}
static void gf_m2ts_process_pes(GF_M2TS_Demuxer *ts, GF_M2TS_PES *pes, GF_M2TS_Header *hdr, unsigned char *data, u32 data_size, GF_M2TS_AdaptationField *paf)
{
u8 expect_cc;
Bool disc=0;
Bool flush_pes = 0;
/*duplicated packet, NOT A DISCONTINUITY, we should discard the packet - however we may encounter this configuration in DASH at segment boundaries.
If payload start is set, ignore duplication*/
if (hdr->continuity_counter==pes->cc) {
if (!hdr->payload_start || (hdr->adaptation_field!=3) ) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: Duplicated Packet found (CC %d) - skipping\n", pes->pid, pes->cc));
return;
}
} else {
expect_cc = (pes->cc<0) ? hdr->continuity_counter : (pes->cc + 1) & 0xf;
if (expect_cc != hdr->continuity_counter)
disc = 1;
}
pes->cc = hdr->continuity_counter;
if (disc) {
if (pes->flags & GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY) {
pes->flags &= ~GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY;
disc = 0;
}
if (disc) {
if (hdr->payload_start) {
if (pes->pck_data_len) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: Packet discontinuity (%d expected - got %d) - may have lost end of previous PES\n", pes->pid, expect_cc, hdr->continuity_counter));
}
} else {
if (pes->pck_data_len) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PES %d: Packet discontinuity (%d expected - got %d) - trashing PES packet\n", pes->pid, expect_cc, hdr->continuity_counter));
}
pes->pck_data_len = 0;
pes->pes_len = 0;
pes->cc = -1;
return;
}
}
}
if (!pes->reframe) return;
if (hdr->payload_start) {
flush_pes = 1;
pes->before_last_pat_pn = pes->last_pat_packet_number;
pes->before_last_pes_start_pn = pes->pes_start_packet_number;
pes->pes_start_packet_number = ts->pck_number;
pes->before_last_pcr_value = pes->program->before_last_pcr_value;
pes->before_last_pcr_value_pck_number = pes->program->before_last_pcr_value_pck_number;
pes->last_pcr_value = pes->program->last_pcr_value;
pes->last_pcr_value_pck_number = pes->program->last_pcr_value_pck_number;
pes->last_pat_packet_number = ts->last_pat_start_num;
} else if (pes->pes_len && (pes->pck_data_len + data_size == pes->pes_len + 6)) {
/* 6 = startcode+stream_id+length*/
/*reassemble pes*/
if (pes->pck_data_len + data_size > pes->pck_alloc_len) {
pes->pck_alloc_len = pes->pck_data_len + data_size;
pes->pck_data = (u8*)gf_realloc(pes->pck_data, pes->pck_alloc_len);
}
memcpy(pes->pck_data+pes->pck_data_len, data, data_size);
pes->pck_data_len += data_size;
/*force discard*/
data_size = 0;
flush_pes = 1;
}
/*PES first fragment: flush previous packet*/
if (flush_pes && pes->pck_data_len) {
gf_m2ts_flush_pes(ts, pes, 1);
if (!data_size) return;
}
/*we need to wait for first packet of PES*/
if (!pes->pck_data_len && !hdr->payload_start) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Waiting for PES header, trashing data\n", hdr->pid));
return;
}
/*reassemble*/
if (pes->pck_data_len + data_size > pes->pck_alloc_len ) {
pes->pck_alloc_len = pes->pck_data_len + data_size;
pes->pck_data = (u8*)gf_realloc(pes->pck_data, pes->pck_alloc_len);
}
memcpy(pes->pck_data + pes->pck_data_len, data, data_size);
pes->pck_data_len += data_size;
if (paf && paf->random_access_indicator) pes->rap = 1;
if (hdr->payload_start && !pes->pes_len && (pes->pck_data_len>=6)) {
pes->pes_len = (pes->pck_data[4]<<8) | pes->pck_data[5];
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Got PES packet len %d\n", pes->pid, pes->pes_len));
if (pes->pes_len + 6 == pes->pck_data_len) {
gf_m2ts_flush_pes(ts, pes, 1);
}
}
}
void gf_m2ts_flush_all(GF_M2TS_Demuxer *ts, Bool no_force_flush)
{
u32 i;
for (i=0; i<GF_M2TS_MAX_STREAMS; i++) {
GF_M2TS_ES *stream = ts->ess[i];
if (stream && (stream->flags & GF_M2TS_ES_IS_PES)) {
gf_m2ts_flush_pes(ts, (GF_M2TS_PES *) stream, no_force_flush ? 0 : 2);
}
}
}
static void gf_m2ts_get_adaptation_field(GF_M2TS_Demuxer *ts, GF_M2TS_AdaptationField *paf, u8 *data, u32 size, u32 pid)
{
unsigned char *af_extension;
paf->discontinuity_indicator = (data[0] & 0x80) ? 1 : 0;
paf->random_access_indicator = (data[0] & 0x40) ? 1 : 0;
paf->priority_indicator = (data[0] & 0x20) ? 1 : 0;
paf->PCR_flag = (data[0] & 0x10) ? 1 : 0;
paf->OPCR_flag = (data[0] & 0x8) ? 1 : 0;
paf->splicing_point_flag = (data[0] & 0x4) ? 1 : 0;
paf->transport_private_data_flag = (data[0] & 0x2) ? 1 : 0;
paf->adaptation_field_extension_flag = (data[0] & 0x1) ? 1 : 0;
af_extension = data + 1;
if (paf->PCR_flag == 1) {
u32 base = ((u32)data[1] << 24) | ((u32)data[2] << 16) | ((u32)data[3] << 8) | (u32) data[4];
u64 PCR = (u64) base;
paf->PCR_base = (PCR << 1) | (data[5] >> 7);
paf->PCR_ext = ((data[5] & 1) << 8) | data[6];
af_extension += 6;
}
if (paf->adaptation_field_extension_flag) {
u32 afext_bytes;
Bool ltw_flag, pwr_flag, seamless_flag, af_desc_not_present;
if (paf->OPCR_flag) {
af_extension += 6;
}
if (paf->splicing_point_flag) {
af_extension += 1;
}
if (paf->transport_private_data_flag) {
u32 priv_bytes = af_extension[0];
af_extension += 1 + priv_bytes;
}
//we need the next 2 bytes
if ((u32)(af_extension-data) >= size-1) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
afext_bytes = af_extension[0];
ltw_flag = (af_extension[1] & 0x80) ? 1 : 0;
pwr_flag = (af_extension[1] & 0x40) ? 1 : 0;
seamless_flag = (af_extension[1] & 0x20) ? 1 : 0;
af_desc_not_present = (af_extension[1] & 0x10) ? 1 : 0;
af_extension += 2;
if (!afext_bytes) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
afext_bytes-=1;
if (ltw_flag) {
af_extension += 2;
if (afext_bytes<2) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
afext_bytes-=2;
}
if (pwr_flag) {
af_extension += 3;
if (afext_bytes<3) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
afext_bytes-=3;
}
if (seamless_flag) {
af_extension += 3;
if (afext_bytes<3) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
afext_bytes-=3;
}
if (! af_desc_not_present) {
if (af_extension + afext_bytes > data+size) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Extension found\n", pid));
return;
}
while (afext_bytes) {
GF_BitStream *bs;
char *desc;
u8 desc_tag = af_extension[0];
u8 desc_len = af_extension[1];
if (!desc_len || (u32) desc_len+2 > afext_bytes) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Bad Adaptation Descriptor found (tag %d) size is %d but only %d bytes available\n", pid, desc_tag, desc_len, afext_bytes));
break;
}
desc = (char *) af_extension+2;
bs = gf_bs_new(desc, desc_len, GF_BITSTREAM_READ);
switch (desc_tag) {
case GF_M2TS_AFDESC_LOCATION_DESCRIPTOR:
{
Bool use_base_temi_url;
char URL[255];
GF_M2TS_TemiLocationDescriptor temi_loc;
memset(&temi_loc, 0, sizeof(GF_M2TS_TemiLocationDescriptor) );
temi_loc.pid = pid;
temi_loc.reload_external = gf_bs_read_int(bs, 1);
temi_loc.is_announce = gf_bs_read_int(bs, 1);
temi_loc.is_splicing = gf_bs_read_int(bs, 1);
use_base_temi_url = gf_bs_read_int(bs, 1);
gf_bs_read_int(bs, 5); //reserved
temi_loc.timeline_id = gf_bs_read_int(bs, 7);
if (temi_loc.is_announce) {
temi_loc.activation_countdown.den = gf_bs_read_u32(bs);
temi_loc.activation_countdown.num = gf_bs_read_u32(bs);
}
if (!use_base_temi_url) {
char *_url = URL;
u8 scheme = gf_bs_read_int(bs, 8);
u8 url_len = gf_bs_read_int(bs, 8);
if (url_len + 4 > desc_len) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Invalid AF Location descriptor (size=%u) found (scheme=%u, url_len=%u))\n", pid, desc_len, scheme, url_len));
break;
}
u8 scheme_len = 0;
switch (scheme) {
case 1:
gf_strcpy(URL, "http://");
scheme_len = 7;
break;
case 2:
gf_strcpy(URL, "https://");
scheme_len = 8;
break;
}
_url = URL + scheme_len;
gf_bs_read_data(bs, _url, url_len);
_url[MIN(url_len, GF_ARRAY_LENGTH(URL)-1-scheme_len)] = 0;
}
temi_loc.external_URL = URL;
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d AF Location descriptor found - URL %s\n", pid, URL));
if (ts->on_event) ts->on_event(ts, GF_M2TS_EVT_TEMI_LOCATION, &temi_loc);
}
break;
case GF_M2TS_AFDESC_TIMELINE_DESCRIPTOR:
if (ts->ess[pid] && (ts->ess[pid]->flags & GF_M2TS_ES_IS_PES)) {
GF_M2TS_PES *pes = (GF_M2TS_PES *) ts->ess[pid];
if (pes->temi_tc_desc_len)
gf_m2ts_store_temi(ts, pes);
if (pes->temi_tc_desc_alloc_size < desc_len) {
pes->temi_tc_desc = gf_realloc(pes->temi_tc_desc, desc_len);
pes->temi_tc_desc_alloc_size = desc_len;
}
memcpy(pes->temi_tc_desc, desc, desc_len);
pes->temi_tc_desc_len = desc_len;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d AF Timeline descriptor found\n", pid));
}
break;
}
gf_bs_del(bs);
af_extension += 2+desc_len;
afext_bytes -= 2+desc_len;
}
}
}
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d: Adaptation Field found: Discontinuity %d - RAP %d - PCR: "LLD"\n", pid, paf->discontinuity_indicator, paf->random_access_indicator, paf->PCR_flag ? paf->PCR_base * 300 + paf->PCR_ext : 0));
}
static void gf_m2ts_reset_parsers_for_program_ex(GF_M2TS_Demuxer *ts, GF_M2TS_Program *prog, Bool keep_pcr);
static GF_Err gf_m2ts_process_packet(GF_M2TS_Demuxer *ts, unsigned char *data)
{
GF_M2TS_ES *es;
GF_M2TS_Header hdr;
GF_M2TS_AdaptationField af, *paf;
u32 payload_size, af_size;
u32 pos = 0;
ts->pck_number++;
/* read TS packet header*/
hdr.sync = data[0];
if (hdr.sync != 0x47) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d does not start with sync marker\n", ts->pck_number));
if (ts->raw_mode == GF_M2TS_RAW_PROBE)
ts->pck_errors++;
else
ts->pck_number--;
return GF_CORRUPTED_DATA;
}
hdr.error = (data[1] & 0x80) ? 1 : 0;
hdr.payload_start = (data[1] & 0x40) ? 1 : 0;
hdr.priority = (data[1] & 0x20) ? 1 : 0;
hdr.pid = ( (data[1]&0x1f) << 8) | data[2];
hdr.scrambling_ctrl = (data[3] >> 6) & 0x3;
hdr.adaptation_field = (data[3] >> 4) & 0x3;
hdr.continuity_counter = data[3] & 0xf;
if (hdr.error) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d has error (PID could be %d)\n", ts->pck_number, hdr.pid));
ts->pck_errors++;
return GF_CORRUPTED_DATA;
}
//#if DEBUG_TS_PACKET
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d PID %d CC %d Encrypted %d\n", ts->pck_number, hdr.pid, hdr.continuity_counter, hdr.scrambling_ctrl));
//#endif
if (hdr.scrambling_ctrl) {
if (ts->raw_mode==GF_M2TS_RAW_FORWARD) {
GF_M2TS_TSPCK tspck;
memset(&tspck, 0, sizeof(GF_M2TS_TSPCK));
tspck.data = data - pos;
ts->on_event(ts, GF_M2TS_EVT_PCK, &tspck);
return GF_OK;
}
//TODO add decyphering API ?
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d is scrambled - not supported\n", ts->pck_number, hdr.pid));
return GF_NOT_SUPPORTED;
}
paf = NULL;
payload_size = 184;
pos = 4;
switch (hdr.adaptation_field) {
/*adaptation+data*/
case 3:
af_size = data[4];
if (af_size>183) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d AF field larger than 183 for AF type 3!\n", ts->pck_number));
//error
ts->pck_errors++;
return GF_CORRUPTED_DATA;
}
if (ts->raw_mode==GF_M2TS_RAW_PROBE) return GF_OK;
paf = &af;
memset(paf, 0, sizeof(GF_M2TS_AdaptationField));
if (af_size) gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size, hdr.pid);
pos += 1+af_size;
payload_size = 183 - af_size;
break;
/*adaptation only - still process in case of PCR*/
case 2:
af_size = data[4];
if (af_size != 183) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] TS Packet %d AF size is %d when it must be 183 for AF type 2\n", ts->pck_number, af_size));
ts->pck_errors++;
return GF_CORRUPTED_DATA;
}
if (ts->raw_mode==GF_M2TS_RAW_PROBE) return GF_OK;
paf = &af;
memset(paf, 0, sizeof(GF_M2TS_AdaptationField));
gf_m2ts_get_adaptation_field(ts, paf, data+5, af_size, hdr.pid);
payload_size = 0;
/*no payload and no PCR, return*/
if (!paf->PCR_flag)
return GF_OK;
break;
/*reserved*/
case 0:
return GF_OK;
default:
if (ts->raw_mode==GF_M2TS_RAW_PROBE) return GF_OK;
break;
}
data += pos;
/*PAT*/
if (hdr.pid == GF_M2TS_PID_PAT) {
if (ts->raw_mode==GF_M2TS_RAW_FORWARD) {
GF_M2TS_TSPCK tspck;
memset(&tspck, 0, sizeof(GF_M2TS_TSPCK));
tspck.data = data - pos;
ts->on_event(ts, GF_M2TS_EVT_PCK, &tspck);
return GF_OK;
}
gf_m2ts_gather_section(ts, ts->pat, NULL, &hdr, data, payload_size);
return GF_OK;
}
es = ts->ess[hdr.pid];
//we work in split mode
if (ts->raw_mode) {
if (ts->raw_mode==GF_M2TS_RAW_PROBE) return GF_OK;
GF_M2TS_TSPCK tspck;
//process PMT table
if (es && (es->flags & GF_M2TS_ES_IS_PMT)) {
GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es;
if (ses->sec) gf_m2ts_gather_section(ts, ses->sec, ses, &hdr, data, payload_size);
}
//and forward every packet other than PAT
memset(&tspck, 0, sizeof(GF_M2TS_TSPCK));
tspck.stream = es;
tspck.pid = hdr.pid;
tspck.data = data - pos;
if (paf && paf->PCR_flag) {
tspck.pcr_plus_one = paf->PCR_base * 300 + paf->PCR_ext;
}
ts->on_event(ts, GF_M2TS_EVT_PCK, &tspck);
return GF_OK;
}
if (hdr.pid == GF_M2TS_PID_CAT) {
gf_m2ts_gather_section(ts, ts->cat, NULL, &hdr, data, payload_size);
return GF_OK;
}
if (paf && paf->PCR_flag) {
if (!es) {
u32 i, j;
for(i=0; i<gf_list_count(ts->programs); i++) {
GF_M2TS_PES *first_pes = NULL;
GF_M2TS_Program *program = (GF_M2TS_Program *)gf_list_get(ts->programs,i);
if(program->pcr_pid != hdr.pid) continue;
for (j=0; j<gf_list_count(program->streams); j++) {
GF_M2TS_PES *pes = (GF_M2TS_PES *) gf_list_get(program->streams, j);
if (pes->flags & GF_M2TS_INHERIT_PCR) {
ts->ess[hdr.pid] = (GF_M2TS_ES *) pes;
pes->flags |= GF_M2TS_FAKE_PCR | GF_M2TS_ES_IS_PCR_REUSE;
break;
}
if (pes->flags & GF_M2TS_ES_IS_PES) {
first_pes = pes;
}
}
//non found, use the first media stream as a PCR destination - Q: is it legal to have PCR only streams not declared in PMT ?
if (!es && first_pes) {
es = (GF_M2TS_ES *) first_pes;
first_pes->flags |= GF_M2TS_FAKE_PCR;
}
break;
}
if (!es)
es = ts->ess[hdr.pid];
}
if (es) {
GF_M2TS_PES_PCK pck;
s64 prev_diff_in_us;
Bool discontinuity;
s32 cc = -1;
if (es->flags & GF_M2TS_FAKE_PCR) {
cc = es->program->pcr_cc;
es->program->pcr_cc = hdr.continuity_counter;
}
else if (es->flags & GF_M2TS_ES_IS_PES) cc = ((GF_M2TS_PES*)es)->cc;
else if (((GF_M2TS_SECTION_ES*)es)->sec) cc = ((GF_M2TS_SECTION_ES*)es)->sec->cc;
discontinuity = paf->discontinuity_indicator;
if ((cc>=0) && es->program->before_last_pcr_value) {
//no increment of CC if AF only packet
if (hdr.adaptation_field == 2) {
if (hdr.continuity_counter != cc) {
discontinuity = GF_TRUE;
}
} else if (hdr.continuity_counter != ((cc + 1) & 0xF)) {
discontinuity = GF_TRUE;
}
}
s64 pcr_diff=0;
memset(&pck, 0, sizeof(GF_M2TS_PES_PCK));
prev_diff_in_us = (s64) (es->program->last_pcr_value - es->program->before_last_pcr_value)/27;
es->program->before_last_pcr_value = es->program->last_pcr_value;
es->program->before_last_pcr_value_pck_number = es->program->last_pcr_value_pck_number;
es->program->last_pcr_value_pck_number = ts->pck_number;
es->program->last_pcr_value = paf->PCR_base * 300 + paf->PCR_ext;
if (!es->program->last_pcr_value) es->program->last_pcr_value = 1;
if (es->program->before_last_pcr_value) {
pcr_diff = es->program->last_pcr_value;
pcr_diff -= es->program->before_last_pcr_value;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR found "LLU" ("LLU" at 90kHz) - PCR diff is %d us\n", hdr.pid, es->program->last_pcr_value, es->program->last_pcr_value/300, (s32) (pcr_diff)/27 ));
} else {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR found "LLU" ("LLU" at 90kHz)\n", hdr.pid, es->program->last_pcr_value, es->program->last_pcr_value/300));
}
pck.PTS = es->program->last_pcr_value;
pck.stream = (GF_M2TS_PES *)es;
//try to ignore all discontinuities that are less than 200 ms (seen in some HLS setup ...)
Bool adjust_pcr=GF_FALSE;
if (discontinuity) {
s64 diff_in_us = (s64) (es->program->last_pcr_value - es->program->before_last_pcr_value) / 27;
u64 diff = ABS(diff_in_us - prev_diff_in_us);
if ((diff_in_us<0) && (diff_in_us >= -200000)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d new PCR, with discontinuity signaled, is less than previously received PCR (diff %d us) but not too large, trying to ignore discontinuity\n", hdr.pid, diff_in_us));
}
//ignore PCR discontinuity indicator if PCR found is larger than previously received PCR and diffence between PCR before and after discontinuity indicator is smaller than 50ms
else if ((diff_in_us > 0) && (diff < 200000)) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR discontinuity signaled but diff is small (diff %d us - PCR diff %d vs prev PCR diff %d) - ignore it\n", hdr.pid, diff, diff_in_us, prev_diff_in_us));
} else if (paf->discontinuity_indicator) {
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR discontinuity signaled (diff %d us - PCR diff %d vs prev PCR diff %d)\n", hdr.pid, diff, diff_in_us, prev_diff_in_us));
pck.flags = GF_M2TS_PES_PCK_DISCONTINUITY;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR discontinuity not signaled (diff %d us - PCR diff %d vs prev PCR diff %d)\n", hdr.pid, diff, diff_in_us, prev_diff_in_us));
pck.flags = GF_M2TS_PES_PCK_DISCONTINUITY;
}
adjust_pcr = GF_TRUE;
}
else if ((es->flags & GF_M2TS_CHECK_DISC) && ABS(pcr_diff)>270000000 ) {
s64 diff_in_us = (s64) (pcr_diff) / 27;
//if less than 200 ms before PCR loop at the last PCR, this is a PCR loop
if (GF_M2TS_MAX_PCR - es->program->before_last_pcr_value < 5400000 /*2*2700000*/) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR loop found from "LLU" to "LLU" \n", hdr.pid, es->program->before_last_pcr_value, es->program->last_pcr_value));
es->program->pcr_base_offset += GF_M2TS_MAX_PCR;
//do not adjust PCR
} else if ((diff_in_us<0) && (diff_in_us >= -200000)) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d new PCR, without discontinuity signaled, is less than previously received PCR (diff %d us) but not too large, trying to ignore discontinuity\n", hdr.pid, diff_in_us));
adjust_pcr = GF_TRUE;
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MPEG-2 TS] PID %d PCR found "LLU" is less than previously received PCR "LLU" (PCR diff %g sec) but no discontinuity signaled\n", hdr.pid, es->program->last_pcr_value, es->program->before_last_pcr_value, (GF_M2TS_MAX_PCR - es->program->before_last_pcr_value + es->program->last_pcr_value) / 27000000.0));
pck.flags = GF_M2TS_PES_PCK_DISCONTINUITY;
adjust_pcr = GF_TRUE;
}
}
//adjust pcr_base_offset to absorb the discontinuity
if (adjust_pcr) {
s64 pcr_base_offset_delta = es->program->before_last_pcr_value + prev_diff_in_us*27;
pcr_base_offset_delta -= es->program->last_pcr_value;
es->program->pcr_base_offset += pcr_base_offset_delta;
}
if (pck.flags & GF_M2TS_PES_PCK_DISCONTINUITY) {
gf_m2ts_reset_parsers_for_program_ex(ts, es->program, GF_TRUE);
}
if (ts->on_event) {
ts->on_event(ts, GF_M2TS_EVT_PES_PCR, &pck);
}
}
}
/*check for DVB reserved PIDs*/
if (!es) {
if (hdr.pid == GF_M2TS_PID_SDT_BAT_ST) {
gf_m2ts_gather_section(ts, ts->sdt, NULL, &hdr, data, payload_size);
return GF_OK;
} else if (hdr.pid == GF_M2TS_PID_NIT_ST) {
/*ignore them, unused at application level*/
gf_m2ts_gather_section(ts, ts->nit, NULL, &hdr, data, payload_size);
return GF_OK;
} else if (hdr.pid == GF_M2TS_PID_EIT_ST_CIT) {
/* ignore EIT messages for the moment */
gf_m2ts_gather_section(ts, ts->eit, NULL, &hdr, data, payload_size);
return GF_OK;
} else if (hdr.pid == GF_M2TS_PID_TDT_TOT_ST) {
gf_m2ts_gather_section(ts, ts->tdt_tot, NULL, &hdr, data, payload_size);
} else {
/* ignore packet */
}
} else if (es->flags & GF_M2TS_ES_IS_SECTION) { /* The stream uses sections to carry its payload */
GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es;
if (ses->sec) gf_m2ts_gather_section(ts, ses->sec, ses, &hdr, data, payload_size);
} else {
GF_M2TS_PES *pes = (GF_M2TS_PES *)es;
/* regular stream using PES packets */
if (pes->reframe && payload_size) gf_m2ts_process_pes(ts, pes, &hdr, data, payload_size, paf);
}
return GF_OK;
}
GF_EXPORT
GF_Err gf_m2ts_process_data(GF_M2TS_Demuxer *ts, u8 *data, u32 data_size)
{
GF_Err e=GF_OK;
u32 pos, pck_size;
Bool is_align = 1;
if (ts->buffer_size) {
//we are sync, copy remaining bytes
if ( (ts->buffer[0]==0x47) && (ts->buffer_size<200)) {
u32 copy_size;
pck_size = ts->prefix_present ? 192 : 188;
if (ts->alloc_size < 200) {
ts->alloc_size = 200;
ts->buffer = (char*)gf_realloc(ts->buffer, sizeof(char)*ts->alloc_size);
}
copy_size = pck_size - ts->buffer_size;
if (copy_size > data_size) {
memcpy(ts->buffer + ts->buffer_size, data, data_size);
ts->buffer_size += data_size;
return GF_OK;
}
memcpy(ts->buffer + ts->buffer_size, data, copy_size);
e |= gf_m2ts_process_packet(ts, (unsigned char *)ts->buffer);
gf_assert(data_size >= copy_size);
data += copy_size;
data_size = data_size - copy_size;
}
//not sync, copy over the complete buffer
else {
if (ts->alloc_size < ts->buffer_size+data_size) {
ts->alloc_size = ts->buffer_size+data_size;
ts->buffer = (char*)gf_realloc(ts->buffer, sizeof(char)*ts->alloc_size);
}
memcpy(ts->buffer + ts->buffer_size, data, sizeof(char)*data_size);
ts->buffer_size += data_size;
is_align = 0;
data = ts->buffer;
data_size = ts->buffer_size;
}
}
/*sync input data*/
pos = gf_m2ts_sync(ts, data, data_size, is_align);
if (pos==data_size) {
if (is_align && data_size) {
if (ts->alloc_size<data_size) {
ts->buffer = (char*)gf_realloc(ts->buffer, sizeof(char)*data_size);
ts->alloc_size = data_size;
}
memcpy(ts->buffer, data, sizeof(char)*data_size);
ts->buffer_size = data_size;
}
return GF_OK;
}
pck_size = ts->prefix_present ? 192 : 188;
for (;;) {
/*wait for a complete packet*/
if (data_size < pos + pck_size) {
ts->buffer_size = data_size - pos;
data += pos;
if (!ts->buffer_size) {
return e;
}
gf_assert(ts->buffer_size<pck_size);
if (is_align) {
u32 s = ts->buffer_size;
if (s<200) s = 200;
if (ts->alloc_size < s) {
ts->alloc_size = s;
ts->buffer = (char*)gf_realloc(ts->buffer, sizeof(char)*ts->alloc_size);
}
memcpy(ts->buffer, data, sizeof(char)*ts->buffer_size);
} else {
memmove(ts->buffer, data, sizeof(char)*ts->buffer_size);
}
return e;
}
/*process*/
GF_Err pck_e = gf_m2ts_process_packet(ts, (unsigned char *)data + pos);
if (pck_e==GF_NOT_SUPPORTED) pck_e = GF_OK;
e |= pck_e;
pos += pck_size;
}
return e;
}
//unused
#if 0
GF_ESD *gf_m2ts_get_esd(GF_M2TS_ES *es)
{
GF_ESD *esd;
u32 k, esd_count;
esd = NULL;
if (es->program->pmt_iod && es->program->pmt_iod->ESDescriptors) {
esd_count = gf_list_count(es->program->pmt_iod->ESDescriptors);
for (k = 0; k < esd_count; k++) {
GF_ESD *esd_tmp = (GF_ESD *)gf_list_get(es->program->pmt_iod->ESDescriptors, k);
if (esd_tmp->ESID != es->mpeg4_es_id) continue;
esd = esd_tmp;
break;
}
}
if (!esd && es->program->additional_ods) {
u32 od_count, od_index;
od_count = gf_list_count(es->program->additional_ods);
for (od_index = 0; od_index < od_count; od_index++) {
GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(es->program->additional_ods, od_index);
esd_count = gf_list_count(od->ESDescriptors);
for (k = 0; k < esd_count; k++) {
GF_ESD *esd_tmp = (GF_ESD *)gf_list_get(od->ESDescriptors, k);
if (esd_tmp->ESID != es->mpeg4_es_id) continue;
esd = esd_tmp;
break;
}
}
}
return esd;
}
void gf_m2ts_set_segment_switch(GF_M2TS_Demuxer *ts)
{
u32 i;
for (i=0; i<GF_M2TS_MAX_STREAMS; i++) {
GF_M2TS_ES *es = (GF_M2TS_ES *) ts->ess[i];
if (!es) continue;
es->flags |= GF_M2TS_ES_IGNORE_NEXT_DISCONTINUITY;
}
}
#endif
static void gf_m2ts_reset_parsers_for_program_ex(GF_M2TS_Demuxer *ts, GF_M2TS_Program *prog, Bool keep_pcr)
{
u32 i;
for (i=0; i<GF_M2TS_MAX_STREAMS; i++) {
GF_M2TS_ES *es = (GF_M2TS_ES *) ts->ess[i];
if (!es) continue;
if (prog && (es->program != prog) ) continue;
if (es->flags & GF_M2TS_ES_IS_SECTION) {
GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es;
gf_m2ts_section_filter_reset(ses->sec);
} else {
GF_M2TS_PES *pes = (GF_M2TS_PES *)es;
if (pes->pid==pes->program->pmt_pid) continue;
pes->cc = -1;
pes->pck_data_len = 0;
if (pes->prev_data) gf_free(pes->prev_data);
pes->prev_data = NULL;
pes->prev_data_len = 0;
pes->PTS = pes->DTS = 0;
// pes->prev_PTS = 0;
// pes->first_dts = 0;
pes->pes_len = pes->pes_end_packet_number = pes->pes_start_packet_number = 0;
if (pes->temi_tc_desc) gf_free(pes->temi_tc_desc);
pes->temi_tc_desc = NULL;
pes->temi_tc_desc_len = pes->temi_tc_desc_alloc_size = 0;
if (keep_pcr) continue;
pes->before_last_pcr_value = pes->before_last_pcr_value_pck_number = 0;
pes->last_pcr_value = pes->last_pcr_value_pck_number = 0;
if (pes->program->pcr_pid==pes->pid) {
pes->program->last_pcr_value = pes->program->last_pcr_value_pck_number = 0;
pes->program->before_last_pcr_value = pes->program->before_last_pcr_value_pck_number = 0;
pes->program->pcr_base_offset = 0;
}
}
}
}
GF_EXPORT
void gf_m2ts_reset_parsers_for_program(GF_M2TS_Demuxer *ts, GF_M2TS_Program *prog)
{
gf_m2ts_reset_parsers_for_program_ex(ts, prog, GF_FALSE);
}
GF_EXPORT
void gf_m2ts_reset_parsers(GF_M2TS_Demuxer *ts)
{
gf_m2ts_reset_parsers_for_program(ts, NULL);
ts->pck_number = 0;
ts->buffer_size = 0;
gf_m2ts_section_filter_reset(ts->cat);
gf_m2ts_section_filter_reset(ts->pat);
gf_m2ts_section_filter_reset(ts->sdt);
gf_m2ts_section_filter_reset(ts->nit);
gf_m2ts_section_filter_reset(ts->eit);
gf_m2ts_section_filter_reset(ts->tdt_tot);
}
void gf_m2ts_mark_seg_start(GF_M2TS_Demuxer *ts)
{
u32 i;
for (i=0; i<GF_M2TS_MAX_STREAMS; i++) {
GF_M2TS_ES *es = (GF_M2TS_ES *) ts->ess[i];
if (!es) continue;
if (es->flags & GF_M2TS_ES_IS_SECTION) {
GF_M2TS_SECTION_ES *ses = (GF_M2TS_SECTION_ES *)es;
ses->is_seg_start = GF_TRUE;
} else {
GF_M2TS_PES *pes = (GF_M2TS_PES *)es;
pes->is_seg_start = GF_TRUE;
}
}
}
#if 0 //unused
u32 gf_m2ts_pes_get_framing_mode(GF_M2TS_PES *pes)
{
if (pes->flags & GF_M2TS_ES_IS_SECTION) {
if (pes->flags & GF_M2TS_ES_IS_SL) {
if ( ((GF_M2TS_SECTION_ES *)pes)->sec->process_section == NULL)
return GF_M2TS_PES_FRAMING_DEFAULT;
}
return GF_M2TS_PES_FRAMING_SKIP_NO_RESET;
}
if (!pes->reframe ) return GF_M2TS_PES_FRAMING_SKIP_NO_RESET;
if (pes->reframe == gf_m2ts_reframe_default) return GF_M2TS_PES_FRAMING_RAW;
if (pes->reframe == gf_m2ts_reframe_reset) return GF_M2TS_PES_FRAMING_SKIP;
return GF_M2TS_PES_FRAMING_DEFAULT;
}
#endif
GF_EXPORT
GF_Err gf_m2ts_set_pes_framing(GF_M2TS_PES *pes, GF_M2TSPesFraming mode)
{
if (!pes) return GF_BAD_PARAM;
GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MPEG-2 TS] Setting pes framing mode of PID %d to %d\n", pes->pid, mode) );
/*ignore request for section PIDs*/
if (pes->flags & GF_M2TS_ES_IS_SECTION) {
if (pes->flags & GF_M2TS_ES_IS_SL) {
if (mode==GF_M2TS_PES_FRAMING_DEFAULT) {
((GF_M2TS_SECTION_ES *)pes)->sec->process_section = gf_m2ts_process_mpeg4section;
} else {
((GF_M2TS_SECTION_ES *)pes)->sec->process_section = NULL;
}
}
else if (mode==GF_M2TS_PES_FRAMING_DEFAULT) {
((GF_M2TS_SECTION_ES *)pes)->sec->process_section = gf_m2ts_process_generic_section;
} else {
((GF_M2TS_SECTION_ES *)pes)->sec->process_section = NULL;
}
return GF_OK;
}
if (pes->pid==pes->program->pmt_pid) return GF_BAD_PARAM;
//if component reuse, disable previous pes
if ((mode > GF_M2TS_PES_FRAMING_SKIP) && (pes->program->ts->ess[pes->pid] != (GF_M2TS_ES *) pes)) {
GF_M2TS_PES *o_pes = (GF_M2TS_PES *) pes->program->ts->ess[pes->pid];
if (o_pes->flags & GF_M2TS_ES_IS_PES)
gf_m2ts_set_pes_framing(o_pes, GF_M2TS_PES_FRAMING_SKIP);
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MPEG-2 TS] Reassinging PID %d from program %d to program %d\n", pes->pid, o_pes->program->number, pes->program->number) );
gf_m2ts_es_del((GF_M2TS_ES*)o_pes, pes->program->ts);
pes->program->ts->ess[pes->pid] = (GF_M2TS_ES *) pes;
}
switch (mode) {
case GF_M2TS_PES_FRAMING_RAW:
pes->reframe = gf_m2ts_reframe_default;
break;
case GF_M2TS_PES_FRAMING_SKIP:
pes->reframe = gf_m2ts_reframe_reset;
break;
case GF_M2TS_PES_FRAMING_SKIP_NO_RESET:
pes->reframe = NULL;
break;
case GF_M2TS_PES_FRAMING_DEFAULT:
default:
switch (pes->stream_type) {
case GF_M2TS_VIDEO_MPEG1:
case GF_M2TS_VIDEO_MPEG2:
case GF_M2TS_VIDEO_H264:
case GF_M2TS_VIDEO_SVC:
case GF_M2TS_VIDEO_HEVC:
case GF_M2TS_VIDEO_HEVC_TEMPORAL:
case GF_M2TS_VIDEO_HEVC_MCTS:
case GF_M2TS_VIDEO_SHVC:
case GF_M2TS_VIDEO_SHVC_TEMPORAL:
case GF_M2TS_VIDEO_MHVC:
case GF_M2TS_VIDEO_MHVC_TEMPORAL:
case GF_M2TS_AUDIO_MPEG1:
case GF_M2TS_AUDIO_MPEG2:
case GF_M2TS_AUDIO_AAC:
case GF_M2TS_AUDIO_LATM_AAC:
case GF_M2TS_AUDIO_AC3:
case GF_M2TS_AUDIO_EC3:
case 0xA1:
//for all our supported codec types, use a reframer filter
pes->reframe = gf_m2ts_reframe_default;
break;
case GF_M2TS_PRIVATE_DATA:
/* TODO: handle DVB subtitle streams */
break;
case GF_M2TS_METADATA_ID3_HLS:
case GF_M2TS_METADATA_ID3_KLVA:
pes->reframe = gf_m2ts_reframe_add_prop;
break;
default:
pes->reframe = gf_m2ts_reframe_default;
break;
}
break;
}
return GF_OK;
}
GF_EXPORT
GF_M2TS_Demuxer *gf_m2ts_demux_new()
{
GF_M2TS_Demuxer *ts;
GF_SAFEALLOC(ts, GF_M2TS_Demuxer);
if (!ts) return NULL;
ts->programs = gf_list_new();
ts->SDTs = gf_list_new();
ts->pat = gf_m2ts_section_filter_new(gf_m2ts_process_pat, 0);
ts->cat = gf_m2ts_section_filter_new(gf_m2ts_process_cat, 0);
ts->sdt = gf_m2ts_section_filter_new(gf_m2ts_process_sdt, 1);
ts->nit = gf_m2ts_section_filter_new(gf_m2ts_process_nit, 0);
ts->eit = gf_m2ts_section_filter_new(NULL/*gf_m2ts_process_eit*/, 1);
ts->tdt_tot = gf_m2ts_section_filter_new(gf_m2ts_process_tdt_tot, 1);
#ifdef GPAC_ENABLE_MPE
gf_dvb_mpe_init(ts);
#endif
ts->nb_prog_pmt_received = 0;
ts->ChannelAppList = gf_list_new();
return ts;
}
GF_EXPORT
void gf_m2ts_demux_dmscc_init(GF_M2TS_Demuxer *ts) {
char temp_dir[GF_MAX_PATH];
u32 length;
GF_Err e;
ts->dsmcc_controler = gf_list_new();
ts->process_dmscc = 1;
gf_strcpy(temp_dir, gf_get_default_cache_directory() );
length = (u32) strlen(temp_dir);
if(temp_dir[length-1] == GF_PATH_SEPARATOR) {
temp_dir[length-1] = 0;
}
ts->dsmcc_root_dir = (char*)gf_calloc(strlen(temp_dir)+strlen("CarouselData")+2,sizeof(char));
sprintf(ts->dsmcc_root_dir,"%s%cCarouselData",temp_dir,GF_PATH_SEPARATOR);
e = gf_mkdir(ts->dsmcc_root_dir);
if(e) {
GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[Process DSMCC] Error during the creation of the directory %s \n",ts->dsmcc_root_dir));
}
}
GF_EXPORT
void gf_m2ts_demux_del(GF_M2TS_Demuxer *ts)
{
u32 i;
if (ts->pat) gf_m2ts_section_filter_del(ts->pat);
if (ts->cat) gf_m2ts_section_filter_del(ts->cat);
if (ts->sdt) gf_m2ts_section_filter_del(ts->sdt);
if (ts->nit) gf_m2ts_section_filter_del(ts->nit);
if (ts->eit) gf_m2ts_section_filter_del(ts->eit);
if (ts->tdt_tot) gf_m2ts_section_filter_del(ts->tdt_tot);
for (i=0; i<GF_M2TS_MAX_STREAMS; i++) {
//because of pure PCR streams, an ES might be reassigned on 2 PIDs, one for the ES and one for the PCR
if (ts->ess[i] && (ts->ess[i]->pid==i)) {
gf_m2ts_es_del(ts->ess[i], ts);
}
}
if (ts->buffer) gf_free(ts->buffer);
while (gf_list_count(ts->programs)) {
GF_M2TS_Program *p = (GF_M2TS_Program *)gf_list_last(ts->programs);
gf_list_rem_last(ts->programs);
while (gf_list_count(p->streams)) {
GF_M2TS_ES *es = (GF_M2TS_ES *)gf_list_last(p->streams);
gf_list_rem_last(p->streams);
//force destroy
es->flags &= ~GF_M2TS_ES_IS_PCR_REUSE;
gf_m2ts_es_del(es, ts);
}
gf_list_del(p->streams);
/*reset OD list*/
if (p->additional_ods) {
gf_odf_desc_list_del(p->additional_ods);
gf_list_del(p->additional_ods);
}
if (p->pmt_iod) gf_odf_desc_del((GF_Descriptor *)p->pmt_iod);
if (p->metadata_pointer_descriptor) gf_m2ts_metadata_pointer_descriptor_del(p->metadata_pointer_descriptor);
gf_free(p);
}
gf_list_del(ts->programs);
if (ts->TDT_time) gf_free(ts->TDT_time);
gf_m2ts_reset_sdt(ts);
if (ts->tdt_tot)
gf_list_del(ts->SDTs);
#ifdef GPAC_ENABLE_MPE
gf_dvb_mpe_shutdown(ts);
#endif
if (ts->dsmcc_controler) {
if (gf_list_count(ts->dsmcc_controler)) {
#ifdef GPAC_ENABLE_DSMCC
GF_M2TS_DSMCC_OVERLORD* dsmcc_overlord = (GF_M2TS_DSMCC_OVERLORD*)gf_list_get(ts->dsmcc_controler,0);
gf_dir_cleanup(dsmcc_overlord->root_dir);
gf_rmdir(dsmcc_overlord->root_dir);
gf_m2ts_delete_dsmcc_overlord(dsmcc_overlord);
if(ts->dsmcc_root_dir) {
gf_free(ts->dsmcc_root_dir);
}
#endif
}
gf_list_del(ts->dsmcc_controler);
}
while(gf_list_count(ts->ChannelAppList)) {
#ifdef GPAC_ENABLE_DSMCC
GF_M2TS_CHANNEL_APPLICATION_INFO* ChanAppInfo = (GF_M2TS_CHANNEL_APPLICATION_INFO*)gf_list_get(ts->ChannelAppList,0);
gf_m2ts_delete_channel_application_info(ChanAppInfo);
gf_list_rem(ts->ChannelAppList,0);
#endif
}
gf_list_del(ts->ChannelAppList);
if (ts->dsmcc_root_dir) gf_free(ts->dsmcc_root_dir);
gf_free(ts);
}
#if 0//unused
void gf_m2ts_print_info(GF_M2TS_Demuxer *ts)
{
#ifdef GPAC_ENABLE_MPE
gf_dvb_mpe_print_info(ts);
#endif
}
#endif
//20 packets max
#define M2TS_PROBE_SIZE 188*20
static Bool gf_m2ts_probe_buffer(char *buf, u32 size)
{
GF_Err e;
GF_M2TS_Demuxer *ts;
u32 lt;
lt = gf_log_get_tool_level(GF_LOG_CONTAINER);
gf_log_set_tool_level(GF_LOG_CONTAINER, GF_LOG_QUIET);
ts = gf_m2ts_demux_new();
ts->raw_mode = GF_M2TS_RAW_PROBE;
e = gf_m2ts_process_data(ts, buf, size);
if (!ts->pck_number) {
e = GF_BAD_PARAM;
} else {
u32 nb_pck;
//max number of packets
if (ts->prefix_present)
nb_pck = size/192;
else
nb_pck = size/188;
//incomplete last packet
if (ts->buffer_size) nb_pck--;
//probe success if after align we have nb_pck - 2 and at least 2 packets
if ((nb_pck<2) || (ts->pck_number + 2 < nb_pck))
e = GF_BAD_PARAM;
//accept if we have not too few errors (triggered on error bit and corrupted AF fields)
else if ((nb_pck>3) && ts->pck_errors*3<ts->pck_number)
e = GF_OK;
}
gf_m2ts_demux_del(ts);
gf_log_set_tool_level(GF_LOG_CONTAINER, lt);
if (e) return GF_FALSE;
return GF_TRUE;
}
GF_EXPORT
Bool gf_m2ts_probe_file(const char *fileName)
{
char buf[M2TS_PROBE_SIZE];
u32 size;
if (!strncmp(fileName, "gmem://", 7)) {
u8 *mem_address;
if (gf_blob_get(fileName, &mem_address, &size, NULL) != GF_OK) {
return GF_FALSE;
}
if (size>M2TS_PROBE_SIZE) size = M2TS_PROBE_SIZE;
memcpy(buf, mem_address, size);
gf_blob_release(fileName);
} else {
FILE *t = gf_fopen(fileName, "rb");
if (!t) return 0;
size = (u32) gf_fread(buf, M2TS_PROBE_SIZE, t);
gf_fclose(t);
if ((s32) size <= 0) return 0;
}
return gf_m2ts_probe_buffer(buf, size);
}
GF_EXPORT
Bool gf_m2ts_probe_data(const u8 *data, u32 size)
{
size /= 188;
size *= 188;
if (!size) return GF_FALSE;
return gf_m2ts_probe_buffer((char *) data, size);
}
static void rewrite_pts_dts(unsigned char *ptr, u64 TS)
{
ptr[0] &= 0xf1;
ptr[0] |= (unsigned char)((TS&0x1c0000000ULL)>>29);
ptr[1] = (unsigned char)((TS&0x03fc00000ULL)>>22);
ptr[2] &= 0x1;
ptr[2] |= (unsigned char)((TS&0x0003f8000ULL)>>14);
ptr[3] = (unsigned char)((TS&0x000007f80ULL)>>7);
ptr[4] &= 0x1;
ptr[4] |= (unsigned char)((TS&0x00000007fULL)<<1);
gf_assert(((u64)(ptr[0]&0xe)<<29) + ((u64)ptr[1]<<22) + ((u64)(ptr[2]&0xfe)<<14) + ((u64)ptr[3]<<7) + ((ptr[4]&0xfe)>>1) == TS);
}
#define ADJUST_TIMESTAMP(_TS) \
if (_TS < (u64) -ts_shift) _TS = pcr_mod + _TS + ts_shift; \
else _TS = _TS + ts_shift; \
while (_TS > pcr_mod) _TS -= pcr_mod; \
GF_EXPORT
GF_Err gf_m2ts_restamp(u8 *buffer, u32 size, s64 ts_shift, u8 is_pes[GF_M2TS_MAX_STREAMS])
{
u32 done = 0;
u64 pcr_mod;
// if (!ts_shift) return GF_OK;
pcr_mod = 0x80000000;
pcr_mod*=4;
while (done + 188 <= size) {
u8 *pesh;
u8 *pck;
u64 pcr_base=0, pcr_ext=0;
u16 pid;
u8 adaptation_field, adaptation_field_length;
pck = (u8*) buffer+done;
if (pck[0]!=0x47) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TS Restamp] Invalid sync byte %X\n", pck[0]));
return GF_NON_COMPLIANT_BITSTREAM;
}
pid = ((pck[1] & 0x1f) <<8 ) + pck[2];
adaptation_field_length = 0;
adaptation_field = (pck[3] >> 4) & 0x3;
if ((adaptation_field==2) || (adaptation_field==3)) {
adaptation_field_length = pck[4];
if ( pck[5]&0x10 /*PCR_flag*/) {
pcr_base = (((u64)pck[6])<<25) + (pck[7]<<17) + (pck[8]<<9) + (pck[9]<<1) + (pck[10]>>7);
pcr_ext = ((pck[10]&1)<<8) + pck[11];
ADJUST_TIMESTAMP(pcr_base);
pck[6] = (unsigned char)(0xff&(pcr_base>>25));
pck[7] = (unsigned char)(0xff&(pcr_base>>17));
pck[8] = (unsigned char)(0xff&(pcr_base>>9));
pck[9] = (unsigned char)(0xff&(pcr_base>>1));
pck[10] = (unsigned char)(((0x1&pcr_base)<<7) | 0x7e | ((0x100&pcr_ext)>>8));
if (pcr_ext != ((pck[10]&1)<<8) + pck[11]) {
GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TS Restamp] Sanity check failed for PCR restamping\n"));
return GF_IO_ERR;
}
pck[11] = (unsigned char)(0xff&pcr_ext);
}
/*add adaptation_field_length field*/
adaptation_field_length++;
}
if (!is_pes[pid] || !(pck[1]&0x40)) {
done+=188;
continue;
}
pesh = &pck[4+adaptation_field_length];
if ((pesh[0]==0x00) && (pesh[1]==0x00) && (pesh[2]==0x01)) {
Bool has_pts, has_dts;
if ((pesh[6]&0xc0)!=0x80) {
done+=188;
continue;
}
has_pts = (pesh[7]&0x80);
has_dts = has_pts ? (pesh[7]&0x40) : 0;
if (has_pts) {
u64 PTS;
if (((pesh[9]&0xe0)>>4)!=0x2) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS Restamp] PID %4d: Wrong PES header, PTS decoding: '0010' expected\n", pid));
done+=188;
continue;
}
PTS = gf_m2ts_get_pts(pesh + 9);
ADJUST_TIMESTAMP(PTS);
rewrite_pts_dts(pesh+9, PTS);
}
if (has_dts) {
u64 DTS = gf_m2ts_get_pts(pesh + 14);
ADJUST_TIMESTAMP(DTS);
rewrite_pts_dts(pesh+14, DTS);
}
} else {
GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TS Restamp] PID %4d: Wrong PES not beginning with start code\n", pid));
}
done+=188;
}
return GF_OK;
}
#endif /*GPAC_DISABLE_MPEG2TS*/