/*************************************************************************************** * * 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); }