628 lines
15 KiB
C++
628 lines
15 KiB
C++
/***************************************************************************************
|
|
*
|
|
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
*
|
|
* By downloading, copying, installing or using the software you agree to this license.
|
|
* If you do not agree to this license, do not download, install,
|
|
* copy or use the software.
|
|
*
|
|
* Copyright (C) 2014-2024, Happytimesoft Corporation, all rights reserved.
|
|
*
|
|
* Redistribution and use in binary forms, with or without modification, are permitted.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software distributed
|
|
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the License.
|
|
*
|
|
****************************************************************************************/
|
|
|
|
#include "ts_parser.h"
|
|
#include "bs.h"
|
|
|
|
|
|
BOOL ts_parser_add_item_to_list(TSList * list, void * data)
|
|
{
|
|
TSListItem * item = (TSListItem *)malloc(sizeof(TSListItem));
|
|
if (NULL == item)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
item->mData = data;
|
|
item->mNext = NULL;
|
|
|
|
if (list->mTail != NULL)
|
|
{
|
|
list->mTail->mNext = item;
|
|
list->mTail = item;
|
|
}
|
|
else
|
|
{
|
|
list->mHead = list->mTail = item;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_add_program(TSParser * parser, uint32 program_map_pid)
|
|
{
|
|
TSProgram * program = (TSProgram *)malloc(sizeof(TSProgram));
|
|
if (NULL == program)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
memset(program, 0, sizeof(TSProgram));
|
|
|
|
program->mProgramMapPID = program_map_pid;
|
|
|
|
return ts_parser_add_item_to_list(&parser->mPrograms, program);
|
|
}
|
|
|
|
BOOL ts_parser_add_stream(TSProgram * program, uint32 elementaryPID, uint32 streamType)
|
|
{
|
|
TSStream * stream = (TSStream *)malloc(sizeof(TSStream));
|
|
if (NULL == stream)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
memset(stream, 0, sizeof(TSStream));
|
|
|
|
stream->mProgram = program;
|
|
stream->mElementaryPID = elementaryPID;
|
|
stream->mStreamType = streamType;
|
|
|
|
// todo : The maximum receive buffer should be allocated based on streamType
|
|
|
|
log_print(HT_LOG_INFO, "%s, add stream, pid: %d, stream type: %d (%X)\r\n",
|
|
__FUNCTION__, elementaryPID, streamType, streamType);
|
|
|
|
return ts_parser_add_item_to_list(&program->mStreams, stream);
|
|
}
|
|
|
|
TSStream * ts_parser_get_stream_by_pid(TSProgram * program, uint32 pid)
|
|
{
|
|
TSListItem *item;
|
|
TSStream *stream;
|
|
|
|
for (item = program->mStreams.mHead; item != NULL; item = item->mNext)
|
|
{
|
|
stream = (TSStream *)item->mData;
|
|
|
|
if (stream != NULL && stream->mElementaryPID == pid)
|
|
{
|
|
return stream;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int64 ts_parser_parse_timestamp(bs_t * bs)
|
|
{
|
|
int64 result = ((uint64)bs_read(bs, 3)) << 30;
|
|
bs_skip(bs, 1);
|
|
result |= ((uint64)bs_read(bs, 15)) << 15;
|
|
bs_skip(bs, 1);
|
|
result |= bs_read(bs, 15);
|
|
|
|
return result;
|
|
}
|
|
|
|
int64 ts_parser_pts_to_timestamp(TSStream * stream, uint64 PTS)
|
|
{
|
|
if (!stream->mProgram->mFirstPTSValid)
|
|
{
|
|
stream->mProgram->mFirstPTSValid = 1;
|
|
stream->mProgram->mFirstPTS = PTS;
|
|
PTS = 0;
|
|
}
|
|
else if (PTS < stream->mProgram->mFirstPTS)
|
|
{
|
|
PTS = 0;
|
|
}
|
|
else
|
|
{
|
|
PTS -= stream->mProgram->mFirstPTS;
|
|
}
|
|
|
|
return (PTS * 100) / 9;
|
|
}
|
|
|
|
void ts_parser_on_payload_data(TSParser * parser, TSStream * stream, uint32 PTS_DTS_flag, uint64 PTS, uint64 DTS, uint8 *data, uint32 size)
|
|
{
|
|
// int64 timeUs = ts_parser_pts_to_timestamp(stream, PTS);
|
|
|
|
sys_os_mutex_enter(parser->mMutex);
|
|
if (parser->mCallback)
|
|
{
|
|
parser->mCallback(data, size, stream->mStreamType, PTS, parser->mUserdata);
|
|
}
|
|
sys_os_mutex_leave(parser->mMutex);
|
|
}
|
|
|
|
void ts_parser_parse_pes(TSParser * parser, TSStream * stream, bs_t * bs)
|
|
{
|
|
bs_read(bs, 24); // packet_startcode_prefix
|
|
|
|
uint32 stream_id = bs_read(bs, 8);
|
|
uint32 PES_packet_length = bs_read(bs, 16);
|
|
|
|
if (stream_id != 0xbc // program_stream_map
|
|
&& stream_id != 0xbe // padding_stream
|
|
&& stream_id != 0xbf // private_stream_2
|
|
&& stream_id != 0xf0 // ECM
|
|
&& stream_id != 0xf1 // EMM
|
|
&& stream_id != 0xff // program_stream_directory
|
|
&& stream_id != 0xf2 // DSMCC
|
|
&& stream_id != 0xf8) // H.222.1 type E
|
|
{
|
|
uint32 PTS_DTS_flags;
|
|
uint32 ESCR_flag;
|
|
uint32 ES_rate_flag;
|
|
uint32 PES_header_data_length;
|
|
uint32 optional_bytes_remaining;
|
|
uint64 PTS = 0, DTS = 0;
|
|
|
|
bs_skip(bs, 8);
|
|
|
|
PTS_DTS_flags = bs_read(bs, 2);
|
|
ESCR_flag = bs_read(bs, 1);
|
|
ES_rate_flag = bs_read(bs, 1);
|
|
bs_read(bs, 1); // DSM_trick_mode_flag
|
|
bs_read(bs, 1); // additional_copy_info_flag
|
|
|
|
bs_skip(bs, 2);
|
|
|
|
PES_header_data_length = bs_read(bs, 8);
|
|
optional_bytes_remaining = PES_header_data_length;
|
|
|
|
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3)
|
|
{
|
|
bs_skip(bs, 4);
|
|
PTS = ts_parser_parse_timestamp(bs);
|
|
bs_skip(bs, 1);
|
|
|
|
optional_bytes_remaining -= 5;
|
|
|
|
if (PTS_DTS_flags == 3)
|
|
{
|
|
bs_skip(bs, 4);
|
|
|
|
DTS = ts_parser_parse_timestamp(bs);
|
|
bs_skip(bs, 1);
|
|
|
|
optional_bytes_remaining -= 5;
|
|
}
|
|
}
|
|
|
|
if (ESCR_flag)
|
|
{
|
|
bs_skip(bs, 2);
|
|
|
|
ts_parser_parse_timestamp(bs);
|
|
|
|
bs_skip(bs, 11);
|
|
|
|
optional_bytes_remaining -= 6;
|
|
}
|
|
|
|
if (ES_rate_flag)
|
|
{
|
|
bs_skip(bs, 24);
|
|
optional_bytes_remaining -= 3;
|
|
}
|
|
|
|
bs_skip(bs, optional_bytes_remaining * 8);
|
|
|
|
// ES data follows.
|
|
if (PES_packet_length != 0)
|
|
{
|
|
uint32 dataLength = PES_packet_length - 3 - PES_header_data_length;
|
|
|
|
// Signaling we have payload data
|
|
ts_parser_on_payload_data(parser, stream, PTS_DTS_flags, PTS, DTS, bs_data(bs), dataLength);
|
|
|
|
bs_skip(bs, dataLength * 8);
|
|
}
|
|
else
|
|
{
|
|
// Signaling we have payload data
|
|
ts_parser_on_payload_data(parser, stream, PTS_DTS_flags, PTS, DTS, bs_data(bs), bs_left(bs) / 8);
|
|
}
|
|
}
|
|
else if (stream_id == 0xbe)
|
|
{
|
|
// padding_stream
|
|
bs_skip(bs, PES_packet_length * 8);
|
|
}
|
|
else
|
|
{
|
|
bs_skip(bs, PES_packet_length * 8);
|
|
}
|
|
}
|
|
|
|
void ts_parser_flush_stream_data(TSParser * parser, TSStream * stream)
|
|
{
|
|
bs_t bs;
|
|
bs_init(&bs, stream->mBuffer, stream->mBufferSize);
|
|
|
|
ts_parser_parse_pes(parser, stream, &bs);
|
|
|
|
stream->mBufferSize = 0;
|
|
}
|
|
|
|
BOOL ts_parser_parse_stream(TSParser * parser, TSStream * stream, uint32 payload_unit_start_indicator, bs_t * bs)
|
|
{
|
|
uint32 payload_size;
|
|
|
|
if (payload_unit_start_indicator)
|
|
{
|
|
if (stream->mPayloadStarted)
|
|
{
|
|
ts_parser_flush_stream_data(parser, stream);
|
|
}
|
|
|
|
stream->mPayloadStarted = 1;
|
|
}
|
|
|
|
if (!stream->mPayloadStarted)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
payload_size = bs_left(bs) / 8;
|
|
|
|
if (stream->mBufferSize + payload_size > ARRAY_SIZE(stream->mBuffer))
|
|
{
|
|
log_print(HT_LOG_WARN, "%s, packet size(%d) is too big!!!\r\n",
|
|
__FUNCTION__, stream->mBufferSize + payload_size);
|
|
return FALSE;
|
|
}
|
|
|
|
memcpy(stream->mBuffer + stream->mBufferSize, bs_data(bs), payload_size);
|
|
stream->mBufferSize += payload_size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_parse_program_map(TSParser * parser, TSProgram * program, bs_t * bs)
|
|
{
|
|
uint32 table_id;
|
|
uint32 section_length;
|
|
uint32 program_info_length;
|
|
size_t infoBytesRemaining;
|
|
uint32 streamType;
|
|
uint32 elementaryPID;
|
|
uint32 ES_info_length;
|
|
uint32 info_bytes_remaining;
|
|
|
|
table_id = bs_read(bs, 8);
|
|
if (TS_PMT_TID != table_id)
|
|
{
|
|
log_print(HT_LOG_ERR, "%s, table_id=%d\r\n", __FUNCTION__, table_id);
|
|
return FALSE;
|
|
}
|
|
|
|
bs_read(bs, 1); // section_syntax_indicator
|
|
|
|
bs_skip(bs, 3); // Reserved
|
|
|
|
section_length = bs_read(bs, 12);
|
|
|
|
bs_skip(bs, 16);// program_number
|
|
bs_skip(bs, 2); // reserved
|
|
bs_skip(bs, 5); // version_number
|
|
bs_skip(bs, 1); // current_next_indicator
|
|
bs_skip(bs, 8); // section_number
|
|
bs_skip(bs, 8); // last_section_number
|
|
bs_skip(bs, 3); // reserved
|
|
bs_skip(bs, 13);// PCR_PID
|
|
bs_skip(bs, 4); // reserved
|
|
|
|
program_info_length = bs_read(bs, 12);
|
|
|
|
bs_skip(bs, program_info_length * 8); // skip descriptors
|
|
|
|
// infoBytesRemaining is the number of bytes that make up the
|
|
// variable length section of ES_infos. It does not include the
|
|
// final CRC.
|
|
infoBytesRemaining = section_length - 9 - program_info_length - 4;
|
|
|
|
while (infoBytesRemaining > 0)
|
|
{
|
|
streamType = bs_read(bs, 8);
|
|
|
|
bs_skip(bs, 3); // reserved
|
|
|
|
elementaryPID = bs_read(bs, 13);
|
|
|
|
bs_skip(bs, 4); // reserved
|
|
|
|
ES_info_length = bs_read(bs, 12);
|
|
|
|
info_bytes_remaining = ES_info_length;
|
|
while (info_bytes_remaining >= 2)
|
|
{
|
|
uint32 descLength;
|
|
|
|
bs_skip(bs, 8); // tag
|
|
|
|
descLength = bs_read(bs, 8);
|
|
|
|
bs_skip(bs, descLength * 8);
|
|
|
|
info_bytes_remaining -= descLength + 2;
|
|
}
|
|
|
|
if (ts_parser_get_stream_by_pid(program, elementaryPID) == NULL)
|
|
{
|
|
ts_parser_add_stream(program, elementaryPID, streamType);
|
|
}
|
|
|
|
infoBytesRemaining -= 5 + ES_info_length;
|
|
}
|
|
|
|
bs_skip(bs, 32); // CRC
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_parse_pmt(TSParser * parser, bs_t * bs)
|
|
{
|
|
size_t i;
|
|
uint32 table_id = bs_read(bs, 8);
|
|
if (TS_PAT_TID != table_id)
|
|
{
|
|
log_print(HT_LOG_ERR, "%s, table_id=%d\r\n", __FUNCTION__, table_id);
|
|
return FALSE;
|
|
}
|
|
|
|
bs_read(bs, 1); // section_syntax_indicator
|
|
bs_skip(bs, 1);
|
|
bs_skip(bs, 2); // reserved
|
|
|
|
uint32 section_length = bs_read(bs, 12);
|
|
|
|
bs_skip(bs, 16);// transport_stream_id
|
|
bs_skip(bs, 2); // reserved
|
|
bs_skip(bs, 5); // version_number
|
|
bs_skip(bs, 1); // current_next_indicator
|
|
bs_skip(bs, 8); // section_number
|
|
bs_skip(bs, 8); // last_section_number
|
|
|
|
size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
|
|
|
|
for (i = 0; i < numProgramBytes / 4; ++i)
|
|
{
|
|
uint32 program_number = bs_read(bs, 16);
|
|
|
|
bs_skip(bs, 3); // reserved
|
|
|
|
if (program_number == 0)
|
|
{
|
|
bs_skip(bs, 13); // network_PID
|
|
}
|
|
else
|
|
{
|
|
uint32 programMapPID = bs_read(bs, 13);
|
|
|
|
ts_parser_add_program(parser, programMapPID);
|
|
}
|
|
}
|
|
|
|
bs_skip(bs, 32); // CRC
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_parse_program(TSParser * parser, bs_t * bs, uint32 pid, uint32 payload_unit_start_indicator)
|
|
{
|
|
BOOL handled = 0;
|
|
TSListItem *item;
|
|
TSProgram *program;
|
|
|
|
if (pid == 0)
|
|
{
|
|
if (payload_unit_start_indicator)
|
|
{
|
|
uint32 skip = bs_read(bs, 8);
|
|
bs_skip(bs, skip * 8);
|
|
}
|
|
|
|
return ts_parser_parse_pmt(parser, bs);
|
|
}
|
|
|
|
for (item = parser->mPrograms.mHead; item != NULL; item = item->mNext)
|
|
{
|
|
program = (TSProgram *)item->mData;
|
|
|
|
if (pid == program->mProgramMapPID)
|
|
{
|
|
if (payload_unit_start_indicator)
|
|
{
|
|
uint32 skip = bs_read(bs, 8);
|
|
bs_skip(bs, skip * 8);
|
|
}
|
|
|
|
handled = ts_parser_parse_program_map(parser, program, bs);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TSStream * pStream = ts_parser_get_stream_by_pid(program, pid);
|
|
if (pStream != NULL)
|
|
{
|
|
handled = ts_parser_parse_stream(parser, pStream, payload_unit_start_indicator, bs);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!handled)
|
|
{
|
|
// log_print(HT_LOG_WARN, "%s, PID 0x%04x not handled.\n", __FUNCTION__, pid);
|
|
}
|
|
|
|
return handled;
|
|
}
|
|
|
|
void ts_parser_parse_adaptation_field(TSParser * parser, bs_t * bs)
|
|
{
|
|
uint32 adaptation_field_length = bs_read(bs, 8);
|
|
if (adaptation_field_length > 0)
|
|
{
|
|
bs_skip(bs, adaptation_field_length * 8);
|
|
}
|
|
}
|
|
|
|
BOOL ts_parser_parse_packet(TSParser * parser, uint8 * data, int size)
|
|
{
|
|
uint32 transport_error_indicator;
|
|
uint32 payload_unit_start_indicator;
|
|
uint32 pid;
|
|
uint32 adaptation_field_control;
|
|
|
|
bs_t bs;
|
|
bs_init(&bs, data, size);
|
|
|
|
bs_read(&bs, 8); // sync_byte
|
|
|
|
transport_error_indicator = bs_read(&bs, 1);
|
|
if (transport_error_indicator != 0)
|
|
{
|
|
log_print(HT_LOG_WARN, "%s, transport error indicator: %u\n",
|
|
__FUNCTION__, transport_error_indicator);
|
|
}
|
|
|
|
payload_unit_start_indicator = bs_read(&bs, 1);
|
|
bs_read(&bs, 1); // transport_priority
|
|
pid = bs_read(&bs, 13);
|
|
bs_read(&bs, 2); // transport_scrambling_control
|
|
adaptation_field_control = bs_read(&bs, 2);
|
|
bs_read(&bs, 4); // continuity_counter
|
|
|
|
if (adaptation_field_control == 2 || adaptation_field_control == 3)
|
|
{
|
|
ts_parser_parse_adaptation_field(parser, &bs);
|
|
}
|
|
|
|
if (adaptation_field_control == 1 || adaptation_field_control == 3)
|
|
{
|
|
ts_parser_parse_program(parser, &bs, pid, payload_unit_start_indicator);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_parse(TSParser * parser, uint8 * data, int size)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (size == 0)
|
|
{
|
|
break;
|
|
}
|
|
else if (size < TS_PACKET_SIZE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (data[0] != TS_SYNC)
|
|
{
|
|
data++;
|
|
size--;
|
|
}
|
|
else
|
|
{
|
|
if (!ts_parser_parse_packet(parser, data, TS_PACKET_SIZE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
data += TS_PACKET_SIZE;
|
|
size -= TS_PACKET_SIZE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL ts_parser_init(TSParser * parser)
|
|
{
|
|
if (NULL == parser)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
memset(parser, 0, sizeof(TSParser));
|
|
|
|
parser->mMutex = sys_os_create_mutex();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ts_parser_set_cb(TSParser * parser, payload_cb cb, void * userdata)
|
|
{
|
|
if (NULL == parser)
|
|
{
|
|
return;
|
|
}
|
|
|
|
sys_os_mutex_enter(parser->mMutex);
|
|
parser->mCallback = cb;
|
|
parser->mUserdata = userdata;
|
|
sys_os_mutex_leave(parser->mMutex);
|
|
}
|
|
|
|
void ts_parser_free_program(TSProgram * program)
|
|
{
|
|
// Free Streams
|
|
TSListItem *item;
|
|
TSStream *stream;
|
|
|
|
while (program->mStreams.mHead != NULL)
|
|
{
|
|
item = program->mStreams.mHead;
|
|
program->mStreams.mHead = item->mNext;
|
|
|
|
if(item->mData != NULL)
|
|
{
|
|
stream = (TSStream *)item->mData;
|
|
free(stream);
|
|
}
|
|
|
|
free(item);
|
|
}
|
|
}
|
|
|
|
void ts_parser_free(TSParser * parser)
|
|
{
|
|
// Free Programs
|
|
TSListItem *item;
|
|
TSProgram *program;
|
|
|
|
while (parser->mPrograms.mHead != NULL)
|
|
{
|
|
item = parser->mPrograms.mHead;
|
|
parser->mPrograms.mHead = item->mNext;
|
|
|
|
if (item->mData != NULL)
|
|
{
|
|
program = (TSProgram *)item->mData;
|
|
ts_parser_free_program(program);
|
|
free(program);
|
|
}
|
|
|
|
free(item);
|
|
}
|
|
|
|
sys_os_destroy_sig_mutex(parser->mMutex);
|
|
}
|
|
|
|
|
|
|
|
|