descriptor_t *language_descriptor_read(descriptor_t *desc, bs_t *b) { if ((desc == NULL) || (b == NULL)) { SAFE_REPORT_TS_ERR(-1); return NULL; } if (desc->tag != ISO_639_LANGUAGE_DESCRIPTOR) { LOG_ERROR_ARGS("Language descriptor has tag 0x%02X instead of expected 0x%02X", desc->tag, ISO_639_LANGUAGE_DESCRIPTOR); SAFE_REPORT_TS_ERR(-50); return NULL; } language_descriptor_t *ld = (language_descriptor_t*) calloc(1, sizeof(language_descriptor_t)); ld->descriptor.tag = desc->tag; ld->descriptor.length = desc->length; ld->_num_languages = ld->descriptor.length / 4; // language takes 4 bytes if (ld->_num_languages > 0) { ld->languages = malloc(ld->_num_languages * sizeof(language_descriptor_t)); for (int i = 0; i < ld->_num_languages; i++) { ld->languages[i].ISO_639_language_code[0] = bs_read_u8(b); ld->languages[i].ISO_639_language_code[1] = bs_read_u8(b); ld->languages[i].ISO_639_language_code[2] = bs_read_u8(b); ld->languages[i].ISO_639_language_code[3] = 0; ld->languages[i].audio_type = bs_read_u8(b); } } free(desc); return (descriptor_t*) ld; }
int es_info_read(elementary_stream_info_t *es, bs_t *b) { int es_info_start = bs_pos(b); es->stream_type = bs_read_u8(b); bs_skip_u(b, 3); es->elementary_PID = bs_read_u(b, 13); bs_skip_u(b, 4); es->ES_info_length = bs_read_u(b, 12); LOG_DEBUG_ARGS ("es_info_read: es_info_start = %d, bs_pos(b) = %d, b->end = %d", es_info_start, bs_pos(b), b->end - b->start); LOG_DEBUG_ARGS ("es_info_read pre-return %d", bs_pos(b) - es_info_start); LOG_DEBUG_ARGS ("es_info_read: PID = %d, streamType = 0x%x, ES_info_length = %d. Calling read_descriptor_loop", es->elementary_PID, es->stream_type, es->ES_info_length); read_descriptor_loop(es->descriptors, b, es->ES_info_length); if (es->ES_info_length > MAX_ES_INFO_LEN) { LOG_ERROR_ARGS("ES info length is 0x%02X, larger than maximum allowed 0x%02X", es->ES_info_length, MAX_ES_INFO_LEN); reportAddErrorLogArgs("ES info length is 0x%02X, larger than maximum allowed 0x%02X", es->ES_info_length, MAX_ES_INFO_LEN); SAFE_REPORT_TS_ERR(-60); return 0; } LOG_DEBUG_ARGS ("es_info_read returning %d", bs_pos(b) - es_info_start); return bs_pos(b) - es_info_start; }
int mpeg2ts_program_register_pid_processor(mpeg2ts_program_t *m2p, uint32_t PID, demux_pid_handler_t *handler) { if (m2p == NULL || m2p->pmt == NULL || handler == NULL) return 0; // if some handler already registered for this PID before, unregister it mpeg2ts_program_unregister_pid_processor(m2p, PID); pid_info_t *pid = pid_info_new(); elementary_stream_info_t *esi = NULL; for (int i=0; i < vqarray_length(m2p->pmt->es_info); i++) { elementary_stream_info_t *tmp = vqarray_get(m2p->pmt->es_info, i); if (tmp != NULL && tmp->elementary_PID == PID) { esi = tmp; break; } } if (esi == NULL) { LOG_ERROR_ARGS("Elementary stream with PID 0x%02X not found in PMT of program %d", PID, m2p->program_number); SAFE_REPORT_TS_ERR(-71); return -1; } pid->es_info = esi; pid->demux_handler = handler; vqarray_add(m2p->pids, pid); return 0; }
int mpeg2ts_stream_read_ts_packet(mpeg2ts_stream_t *m2s, ts_packet_t *ts) { int ret = 0; if (m2s == NULL || ts == NULL) return 0; if ((m2s->pat == NULL) && (ts->header.PID != PAT_PID)) return 0; // we don't have any PSI, nothing we can do. pid_info_t *pi = NULL; for (int i = 0; i < vqarray_length(m2s->programs) && (pi == NULL); i++) { mpeg2ts_program_t *m2p = vqarray_get(m2s->programs, i); if (m2p == NULL) continue; if (m2p->PID == ts->header.PID) { // got a PMT // FIXME: this hack works in 90% of cases // however, it won't work when PAT is // in 2+ packets. we need generic section reader for this program_map_section_t *pms = program_map_section_new(); program_map_section_read(pms, ts->payload.bytes + 1, ts->payload.len - 1); if (pms->program_number != m2p->program_number) { LOG_ERROR_ARGS("Invalid program number in PMT: expecting %d, encountered %d", m2p->program_number, pms->program_number); SAFE_REPORT_TS_ERR(-70); return -1; } if (m2p->pmt != NULL) { // FIXME: compare sections, drop the older one if ( pms->version_number != m2p->pmt->version_number ) LOG_WARN("Changing PMT section versions not supported yet"); program_map_section_free(pms); continue; } else { m2p->pmt = pms; for (int es_idx=0; es_idx < vqarray_length(m2p->pmt->es_info) ; es_idx++) { elementary_stream_info_t *es = vqarray_get(m2p->pmt->es_info, es_idx); if (es == NULL) continue; pi = pid_info_new(); pi->es_info = es; // FIXME add default stream handlers via stream type vqarray_add(m2p->pids, pi); pi = NULL; } } } else { // same elementary PID may be owned by multiple programs pi = mpeg2ts_program_get_pid_info(m2p, ts->header.PID); if ((pi != NULL) && (pi->demux_handler != NULL) && (pi->demux_handler->process_ts_packet != NULL)) { ret = pi->demux_handler->process_ts_packet(ts, pi->es_info, pi->demux_handler->arg); if (ret != 0) return ret; } } } return ret; }
int es_info_read(elementary_stream_info_t *es, bs_t *b) { int es_info_start = bs_pos(b); es->stream_type = bs_read_u8(b); bs_skip_u(b, 3); es->elementary_PID = bs_read_u(b, 13); bs_skip_u(b, 4); es->ES_info_length = bs_read_u(b, 12); read_descriptor_loop(es->descriptors, b, es->ES_info_length); if (es->ES_info_length > MAX_ES_INFO_LEN) { LOG_ERROR_ARGS("ES info length is 0x%02X, larger than maximum allowed 0x%02X", es->ES_info_length, MAX_ES_INFO_LEN); SAFE_REPORT_TS_ERR(-60); return 0; } return bs_pos(b) - es_info_start; }
int conditional_access_section_read(conditional_access_section_t *cas, uint8_t *buf, size_t buf_len, uint32_t payload_unit_start_indicator, psi_table_buffer_t *catBuffer) { if (cas == NULL || buf == NULL) { SAFE_REPORT_TS_ERR(-1); return 0; } bs_t *b = NULL; if (!payload_unit_start_indicator && catBuffer->buffer == NULL) { // this TS packet is not start of table, and we have no cached table data LOG_WARN ("conditional_access_section_read: payload_unit_start_indicator not set and no cached data"); return 0; } if (payload_unit_start_indicator) { uint8_t payloadStartPtr = buf[0]; buf += (payloadStartPtr + 1); buf_len -= (payloadStartPtr + 1); LOG_DEBUG_ARGS ("conditional_access_section_read: payloadStartPtr = %d", payloadStartPtr); } // check for pat spanning multiple TS packets if (catBuffer->buffer != NULL) { LOG_DEBUG_ARGS ("conditional_access_section_read: catBuffer detected: catBufferAllocSz = %d, catBufferUsedSz = %d", catBuffer->bufferAllocSz, catBuffer->bufferUsedSz); size_t numBytesToCopy = buf_len; if (buf_len > (catBuffer->bufferAllocSz - catBuffer->bufferUsedSz)) { numBytesToCopy = catBuffer->bufferAllocSz - catBuffer->bufferUsedSz; } LOG_DEBUG_ARGS ("conditional_access_section_read: copying %d bytes to catBuffer", numBytesToCopy); memcpy (catBuffer->buffer + catBuffer->bufferUsedSz, buf, numBytesToCopy); catBuffer->bufferUsedSz += numBytesToCopy; if (catBuffer->bufferUsedSz < catBuffer->bufferAllocSz) { LOG_DEBUG ("conditional_access_section_read: catBuffer not yet full -- returning"); return 0; } b = bs_new(catBuffer->buffer, catBuffer->bufferUsedSz); } else { b = bs_new(buf, buf_len); } cas->table_id = bs_read_u8(b); if (cas->table_id != conditional_access_section) { LOG_ERROR_ARGS("Table ID in CAT is 0x%02X instead of expected 0x%02X", cas->table_id, conditional_access_section); reportAddErrorLogArgs("Table ID in CAT is 0x%02X instead of expected 0x%02X", cas->table_id, conditional_access_section); SAFE_REPORT_TS_ERR(-30); resetPSITableBuffer(catBuffer); bs_free (b); return 0; } // read byte 0 cas->section_syntax_indicator = bs_read_u1(b); if (!cas->section_syntax_indicator) { LOG_ERROR("section_syntax_indicator not set in CAT"); reportAddErrorLog("section_syntax_indicator not set in CAT"); SAFE_REPORT_TS_ERR(-31); resetPSITableBuffer(catBuffer); bs_free (b); return 0; } bs_skip_u(b, 3); // TODO read the zero bit, check it to be zero cas->section_length = bs_read_u(b, 12); if (cas->section_length > 1021) // max CAT length { LOG_ERROR_ARGS("CAT section length is 0x%02X, larger than maximum allowed 0x%02X", cas->section_length, MAX_SECTION_LEN); reportAddErrorLogArgs("CAT section length is 0x%02X, larger than maximum allowed 0x%02X", cas->section_length, MAX_SECTION_LEN); SAFE_REPORT_TS_ERR(-32); resetPSITableBuffer(catBuffer); bs_free (b); return 0; } if (cas->section_length > bs_bytes_left(b)) { LOG_DEBUG ("conditional_access_section_read: Detected section spans more than one TS packet -- allocating buffer"); if (catBuffer->buffer != NULL) { // should never get here LOG_ERROR ("conditional_access_section_read: unexpected catBufffer"); reportAddErrorLog ("conditional_access_section_read: unexpected catBufffer"); resetPSITableBuffer(catBuffer); } catBuffer->bufferAllocSz = cas->section_length + 3; catBuffer->buffer = (uint8_t *)calloc (cas->section_length + 3, 1); memcpy (catBuffer->buffer, buf, buf_len); catBuffer->bufferUsedSz = buf_len; bs_free (b); return 0; } // read bytes 1-2 bs_read_u16(b); // read bytes 3,4 bs_skip_u(b, 2); cas->version_number = bs_read_u(b, 5); cas->current_next_indicator = bs_read_u1(b); if (!cas->current_next_indicator) LOG_WARN("This CAT is not yet applicable/n"); // read byte 5 cas->section_number = bs_read_u8(b); cas->last_section_number = bs_read_u8(b); if (cas->section_number != 0 || cas->last_section_number != 0) LOG_WARN("Multi-section CAT is not supported yet/n"); // read bytes 6,7 read_descriptor_loop(cas->descriptors, b, cas->section_length - 5 - 4 ); // explanation: section_length gives us the length from the end of section_length // we used 5 bytes for the mandatory section fields, and will use another 4 bytes for CRC // the remaining bytes contain descriptors, most probably only one // again, it's much shorter in C :-) cas->CRC_32 = bs_read_u32(b); // check CRC crc_t cas_crc = crc_init(); cas_crc = crc_update(cas_crc, buf, bs_pos(b) - 4); cas_crc = crc_finalize(cas_crc); if (cas_crc != cas->CRC_32) { LOG_ERROR_ARGS("CAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", cas->CRC_32, cas_crc); reportAddErrorLogArgs("CAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", cas->CRC_32, cas_crc); SAFE_REPORT_TS_ERR(-33); resetPSITableBuffer(catBuffer); bs_free (b); return 0; } bs_free(b); resetPSITableBuffer(catBuffer); return 1; }
int program_map_section_read(program_map_section_t *pms, uint8_t *buf, size_t buf_size, uint32_t payload_unit_start_indicator, psi_table_buffer_t *pmtBuffer) { LOG_DEBUG ("program_map_section_read -- entering"); if (pms == NULL || buf == NULL) { SAFE_REPORT_TS_ERR(-1); return 0; } bs_t *b = NULL; if (!payload_unit_start_indicator && pmtBuffer->buffer == NULL) { // this TS packet is not start of table, and we have no cached table data LOG_WARN ("program_map_section_read: payload_unit_start_indicator not set and no cached data"); return 0; } if (payload_unit_start_indicator) { uint8_t payloadStartPtr = buf[0]; buf += (payloadStartPtr + 1); buf_size -= (payloadStartPtr + 1); LOG_DEBUG_ARGS ("program_map_section_read: payloadStartPtr = %d", payloadStartPtr); } // check for pmt spanning multiple TS packets if (pmtBuffer->buffer != NULL) { LOG_DEBUG_ARGS ("program_map_section_read: pmtBuffer detected: pmtBufferAllocSz = %d, pmtBufferUsedSz = %d", pmtBuffer->bufferAllocSz, pmtBuffer->bufferUsedSz); size_t numBytesToCopy = buf_size; if (buf_size > (pmtBuffer->bufferAllocSz - pmtBuffer->bufferUsedSz)) { numBytesToCopy = pmtBuffer->bufferAllocSz - pmtBuffer->bufferUsedSz; } LOG_DEBUG_ARGS ("program_map_section_read: copying %d bytes to pmtBuffer", numBytesToCopy); memcpy (pmtBuffer->buffer + pmtBuffer->bufferUsedSz, buf, numBytesToCopy); pmtBuffer->bufferUsedSz += numBytesToCopy; if (pmtBuffer->bufferUsedSz < pmtBuffer->bufferAllocSz) { LOG_DEBUG ("program_map_section_read: pmtBuffer not yet full -- returning"); return 0; } b = bs_new(pmtBuffer->buffer, pmtBuffer->bufferUsedSz); } else { b = bs_new(buf, buf_size); } pms->table_id = bs_read_u8(b); if (pms->table_id != TS_program_map_section) { LOG_ERROR_ARGS("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section); reportAddErrorLogArgs("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section); SAFE_REPORT_TS_ERR(-40); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } pms->section_syntax_indicator = bs_read_u1(b); if (!pms->section_syntax_indicator) { LOG_ERROR("section_syntax_indicator not set in PMT"); reportAddErrorLog("section_syntax_indicator not set in PMT"); SAFE_REPORT_TS_ERR(-41); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } bs_skip_u(b, 3); pms->section_length = bs_read_u(b, 12); if (pms->section_length > MAX_SECTION_LEN) { LOG_ERROR_ARGS("PMT section length is 0x%02X, larger than maximum allowed 0x%02X", pms->section_length, MAX_SECTION_LEN); reportAddErrorLogArgs("PMT section length is 0x%02X, larger than maximum allowed 0x%02X", pms->section_length, MAX_SECTION_LEN); SAFE_REPORT_TS_ERR(-42); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } if (pms->section_length > bs_bytes_left(b)) { LOG_DEBUG ("program_map_section_read: Detected section spans more than one TS packet -- allocating buffer"); if (pmtBuffer->buffer != NULL) { // should never get here LOG_ERROR ("program_map_section_read: unexpected pmtBufffer"); reportAddErrorLog ("program_map_section_read: unexpected pmtBufffer"); resetPSITableBuffer(pmtBuffer); } pmtBuffer->bufferAllocSz = pms->section_length + 3; pmtBuffer->buffer = (uint8_t *)calloc (pms->section_length + 3, 1); memcpy (pmtBuffer->buffer, buf, buf_size); pmtBuffer->bufferUsedSz = buf_size; bs_free (b); return 0; } int section_start = bs_pos(b); // bytes 0,1 pms->program_number = bs_read_u16(b); // byte 2; bs_skip_u(b, 2); pms->version_number = bs_read_u(b, 5); pms->current_next_indicator = bs_read_u1(b); if (!pms->current_next_indicator) LOG_WARN("This PMT is not yet applicable/n"); // bytes 3,4 pms->section_number = bs_read_u8(b); pms->last_section_number = bs_read_u8(b); if (pms->section_number != 0 || pms->last_section_number != 0) { LOG_ERROR("Multi-section PMT is not allowed/n"); reportAddErrorLog("Multi-section PMT is not allowed/n"); SAFE_REPORT_TS_ERR(-43); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } bs_skip_u(b, 3); pms->PCR_PID = bs_read_u(b, 13); if (pms->PCR_PID < GENERAL_PURPOSE_PID_MIN || pms->PCR_PID > GENERAL_PURPOSE_PID_MAX) { LOG_ERROR_ARGS("PCR PID has invalid value 0x%02X", pms->PCR_PID); reportAddErrorLogArgs("PCR PID has invalid value 0x%02X", pms->PCR_PID); SAFE_REPORT_TS_ERR(-44); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } // printf ("PCR PID = %d\n", pms->PCR_PID); bs_skip_u(b, 4); pms->program_info_length = bs_read_u(b, 12); if (pms->program_info_length > MAX_PROGRAM_INFO_LEN) { LOG_ERROR_ARGS("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X", pms->program_info_length, MAX_PROGRAM_INFO_LEN); reportAddErrorLogArgs("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X", pms->program_info_length, MAX_PROGRAM_INFO_LEN); SAFE_REPORT_TS_ERR(-45); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } read_descriptor_loop(pms->descriptors, b, pms->program_info_length); while (!bs_eof(b) && pms->section_length - (bs_pos(b) - section_start) > 4) // account for CRC { elementary_stream_info_t *es = es_info_new(); es_info_read(es, b); vqarray_add(pms->es_info, es); } pms->CRC_32 = bs_read_u32(b); // check CRC crc_t pas_crc = crc_init(); pas_crc = crc_update(pas_crc, b->start, bs_pos(b) - 4); pas_crc = crc_finalize(pas_crc); if (pas_crc != pms->CRC_32) { LOG_ERROR_ARGS("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc); reportAddErrorLogArgs("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc); SAFE_REPORT_TS_ERR(-46); resetPSITableBuffer(pmtBuffer); bs_free (b); return 0; } else { // LOG_DEBUG("PMT CRC_32 checked successfully"); } int bytes_read = bs_pos(b); bs_free(b); resetPSITableBuffer(pmtBuffer); return bytes_read; }
int program_association_section_read(program_association_section_t *pas, uint8_t *buf, size_t buf_len, uint32_t payload_unit_start_indicator, psi_table_buffer_t *patBuffer) { vqarray_t *programs; int num_programs = 0; if (pas == NULL || buf == NULL) { SAFE_REPORT_TS_ERR(-1); return 0; } bs_t *b = NULL; if (!payload_unit_start_indicator && patBuffer->buffer == NULL) { // this TS packet is not start of table, and we have no cached table data LOG_WARN ("program_association_section_read: payload_unit_start_indicator not set and no cached data"); return 0; } if (payload_unit_start_indicator) { uint8_t payloadStartPtr = buf[0]; buf += (payloadStartPtr + 1); buf_len -= (payloadStartPtr + 1); LOG_DEBUG_ARGS ("program_association_section_read: payloadStartPtr = %d", payloadStartPtr); } // check for pat spanning multiple TS packets if (patBuffer->buffer != NULL) { LOG_DEBUG_ARGS ("program_association_section_read: patBuffer detected: patBufferAllocSz = %d, patBufferUsedSz = %d", patBuffer->bufferAllocSz, patBuffer->bufferUsedSz); size_t numBytesToCopy = buf_len; if (buf_len > (patBuffer->bufferAllocSz - patBuffer->bufferUsedSz)) { numBytesToCopy = patBuffer->bufferAllocSz - patBuffer->bufferUsedSz; } LOG_DEBUG_ARGS ("program_association_section_read: copying %d bytes to patBuffer", numBytesToCopy); memcpy (patBuffer->buffer + patBuffer->bufferUsedSz, buf, numBytesToCopy); patBuffer->bufferUsedSz += numBytesToCopy; if (patBuffer->bufferUsedSz < patBuffer->bufferAllocSz) { LOG_DEBUG ("program_association_section_read: patBuffer not yet full -- returning"); return 0; } b = bs_new(patBuffer->buffer, patBuffer->bufferUsedSz); } else { b = bs_new(buf, buf_len); } pas->table_id = bs_read_u8(b); if (pas->table_id != program_association_section) { LOG_ERROR_ARGS("Table ID in PAT is 0x%02X instead of expected 0x%02X", pas->table_id, program_association_section); reportAddErrorLogArgs("Table ID in PAT is 0x%02X instead of expected 0x%02X", pas->table_id, program_association_section); SAFE_REPORT_TS_ERR(-30); resetPSITableBuffer(patBuffer); bs_free (b); return 0; } // read byte 0 pas->section_syntax_indicator = bs_read_u1(b); if (!pas->section_syntax_indicator) { LOG_ERROR("section_syntax_indicator not set in PAT"); reportAddErrorLog("section_syntax_indicator not set in PAT"); SAFE_REPORT_TS_ERR(-31); resetPSITableBuffer(patBuffer); bs_free (b); return 0; } bs_skip_u(b, 3); // TODO read the zero bit, check it to be zero pas->section_length = bs_read_u(b, 12); if (pas->section_length > MAX_SECTION_LEN) { LOG_ERROR_ARGS("PAT section length is 0x%02X, larger than maximum allowed 0x%02X", pas->section_length, MAX_SECTION_LEN); reportAddErrorLogArgs("PAT section length is 0x%02X, larger than maximum allowed 0x%02X", pas->section_length, MAX_SECTION_LEN); SAFE_REPORT_TS_ERR(-32); resetPSITableBuffer(patBuffer); bs_free (b); return 0; } if (pas->section_length > bs_bytes_left(b)) { LOG_DEBUG ("program_association_section_read: Detected section spans more than one TS packet -- allocating buffer"); if (patBuffer->buffer != NULL) { // should never get here LOG_ERROR ("program_association_section_read: unexpected patBufffer"); reportAddErrorLog ("program_association_section_read: unexpected patBufffer"); resetPSITableBuffer(patBuffer); } patBuffer->bufferAllocSz = pas->section_length + 3; patBuffer->buffer = (uint8_t *)calloc (pas->section_length + 3, 1); memcpy (patBuffer->buffer, buf, buf_len); patBuffer->bufferUsedSz = buf_len; bs_free (b); return 0; } // read bytes 1,2 pas->transport_stream_id = bs_read_u16(b); // read bytes 3,4 bs_skip_u(b, 2); pas->version_number = bs_read_u(b, 5); pas->current_next_indicator = bs_read_u1(b); if (!pas->current_next_indicator) LOG_WARN("This PAT is not yet applicable/n"); // read byte 5 pas->section_number = bs_read_u8(b); pas->last_section_number = bs_read_u8(b); if (pas->section_number != 0 || pas->last_section_number != 0) LOG_WARN("Multi-section PAT is not supported yet/n"); // read bytes 6,7 num_programs = (pas->section_length - 5 - 4) / 4; // Programs listed in the PAT // explanation: section_length gives us the length from the end of section_length // we used 5 bytes for the mandatory section fields, and will use another 4 bytes for CRC // the remaining bytes contain program information, which is 4 bytes per iteration // It's much shorter in C :-) // Read the program loop, but ignore the NIT PID "program" programs = vqarray_new(); for (uint32_t i = 0; i < num_programs; i++) { program_info_t *prog = malloc(sizeof(program_info_t)); prog->program_number = bs_read_u16(b); if (prog->program_number == 0) { // Skip the NIT PID program (not a real program) free(prog); bs_skip_u(b, 16); continue; } bs_skip_u(b, 3); prog->program_map_PID = bs_read_u(b, 13); vqarray_add(programs, (vqarray_elem_t*)prog); } // This is our true number of programs pas->_num_programs = vqarray_length(programs); if (pas->_num_programs > 1) LOG_WARN_ARGS("%zd programs found, but only SPTS is fully supported. Patches are welcome.", pas->_num_programs); // Copy form our vqarray into the native array pas->programs = malloc(pas->_num_programs * sizeof(program_info_t)); for (uint32_t i = 0; i < pas->_num_programs; i++) { program_info_t* prog = (program_info_t*)vqarray_pop(programs); pas->programs[i] = *prog; free(prog); } vqarray_free(programs); pas->CRC_32 = bs_read_u32(b); // check CRC crc_t pas_crc = crc_init(); pas_crc = crc_update(pas_crc, buf, bs_pos(b) - 4); pas_crc = crc_finalize(pas_crc); if (pas_crc != pas->CRC_32) { LOG_ERROR_ARGS("PAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pas->CRC_32, pas_crc); reportAddErrorLogArgs("PAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pas->CRC_32, pas_crc); SAFE_REPORT_TS_ERR(-33); resetPSITableBuffer(patBuffer); bs_free (b); return 0; } else { // LOG_DEBUG("PAT CRC_32 checked successfully"); // don't enable unless you want to see this every ~100ms } bs_free(b); resetPSITableBuffer(patBuffer); return 1; }
int program_map_section_read(program_map_section_t *pms, uint8_t *buf, size_t buf_size) { if (pms == NULL || buf == NULL) { SAFE_REPORT_TS_ERR(-1); return 0; } bs_t *b = bs_new(buf, buf_size); pms->table_id = bs_read_u8(b); if (pms->table_id != TS_program_map_section) { LOG_ERROR_ARGS("Table ID in PMT is 0x%02X instead of expected 0x%02X", pms->table_id, TS_program_map_section); SAFE_REPORT_TS_ERR(-40); return 0; } pms->section_syntax_indicator = bs_read_u1(b); if (!pms->section_syntax_indicator) { LOG_ERROR("section_syntax_indicator not set in PMT"); SAFE_REPORT_TS_ERR(-41); return 0; } bs_skip_u(b, 3); pms->section_length = bs_read_u(b, 12); if (pms->section_length > MAX_SECTION_LEN) { LOG_ERROR_ARGS("PMT section length is 0x%02X, larger than maximum allowed 0x%02X", pms->section_length, MAX_SECTION_LEN); SAFE_REPORT_TS_ERR(-42); return 0; } int section_start = bs_pos(b); // bytes 0,1 pms->program_number = bs_read_u16(b); // byte 2; bs_skip_u(b, 2); pms->version_number = bs_read_u(b, 5); pms->current_next_indicator = bs_read_u1(b); if (!pms->current_next_indicator) LOG_WARN("This PMT is not yet applicable/n"); // bytes 3,4 pms->section_number = bs_read_u8(b); pms->last_section_number = bs_read_u8(b); if (pms->section_number != 0 || pms->last_section_number != 0) { LOG_ERROR("Multi-section PMT is not allowed/n"); SAFE_REPORT_TS_ERR(-43); return 0; } bs_skip_u(b, 3); pms->PCR_PID = bs_read_u(b, 13); if (pms->PCR_PID < GENERAL_PURPOSE_PID_MIN || pms->PCR_PID > GENERAL_PURPOSE_PID_MAX) { LOG_ERROR_ARGS("PCR PID has invalid value 0x%02X", pms->PCR_PID); SAFE_REPORT_TS_ERR(-44); return 0; } bs_skip_u(b, 4); pms->program_info_length = bs_read_u(b, 12); if (pms->program_info_length > MAX_PROGRAM_INFO_LEN) { LOG_ERROR_ARGS("PMT program info length is 0x%02X, larger than maximum allowed 0x%02X", pms->program_info_length, MAX_PROGRAM_INFO_LEN); SAFE_REPORT_TS_ERR(-45); return 0; } read_descriptor_loop(pms->descriptors, b, pms->program_info_length); while (pms->section_length - (bs_pos(b) - section_start) > 4) { // account for CRC elementary_stream_info_t *es = es_info_new(); es_info_read(es, b); vqarray_add(pms->es_info, es); } pms->CRC_32 = bs_read_u32(b); // check CRC crc_t pas_crc = crc_init(); pas_crc = crc_update(pas_crc, buf, bs_pos(b) - 4); pas_crc = crc_finalize(pas_crc); if (pas_crc != pms->CRC_32) { LOG_ERROR_ARGS("PMT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pms->CRC_32, pas_crc); SAFE_REPORT_TS_ERR(-46); return 0; } else { LOG_DEBUG("PMT CRC_32 checked successfully"); } int bytes_read = bs_pos(b); bs_free(b); return bytes_read; }
int program_association_section_read(program_association_section_t *pas, uint8_t *buf, size_t buf_len) { if (pas == NULL || buf == NULL) { SAFE_REPORT_TS_ERR(-1); return 0; } bs_t *b = bs_new(buf, buf_len); pas->table_id = bs_read_u8(b); if (pas->table_id != program_association_section) { LOG_ERROR_ARGS("Table ID in PAT is 0x%02X instead of expected 0x%02X", pas->table_id, program_association_section); SAFE_REPORT_TS_ERR(-30); return 0; } // read byte 0 pas->section_syntax_indicator = bs_read_u1(b); if (!pas->section_syntax_indicator) { LOG_ERROR("section_syntax_indicator not set in PAT"); SAFE_REPORT_TS_ERR(-31); return 0; } bs_skip_u(b, 3); // TODO read the zero bit, check it to be zero pas->section_length = bs_read_u(b, 12); if (pas->section_length > MAX_SECTION_LEN) { LOG_ERROR_ARGS("PAT section length is 0x%02X, larger than maximum allowed 0x%02X", pas->section_length, MAX_SECTION_LEN); SAFE_REPORT_TS_ERR(-32); return 0; } // read bytes 1,2 pas->transport_stream_id = bs_read_u16(b); // read bytes 3,4 bs_skip_u(b, 2); pas->version_number = bs_read_u(b, 5); pas->current_next_indicator = bs_read_u1(b); if (!pas->current_next_indicator) LOG_WARN("This PAT is not yet applicable/n"); // read byte 5 pas->section_number = bs_read_u8(b); pas->last_section_number = bs_read_u8(b); if (pas->section_number != 0 || pas->last_section_number != 0) LOG_WARN("Multi-section PAT is not supported yet/n"); // read bytes 6,7 pas->_num_programs = (pas->section_length - 5 - 4) / 4; // explanation: section_length gives us the length from the end of section_length // we used 5 bytes for the mandatory section fields, and will use another 4 bytes for CRC // the remaining bytes contain program information, which is 4 bytes per iteration // It's much shorter in C :-) if (pas->_num_programs > 1) LOG_WARN_ARGS("%zd programs found, but only SPTS is fully supported. Patches are welcome.", pas->_num_programs); pas->programs = malloc(pas->_num_programs * sizeof(program_info_t)); for (uint32_t i = 0; i < pas->_num_programs; i++) { pas->programs[i].program_number = bs_read_u16(b); bs_skip_u(b, 3); pas->programs[i].program_map_PID = bs_read_u(b, 13); } pas->CRC_32 = bs_read_u32(b); // check CRC crc_t pas_crc = crc_init(); pas_crc = crc_update(pas_crc, buf, bs_pos(b) - 4); pas_crc = crc_finalize(pas_crc); if (pas_crc != pas->CRC_32) { LOG_ERROR_ARGS("PAT CRC_32 specified as 0x%08X, but calculated as 0x%08X", pas->CRC_32, pas_crc); SAFE_REPORT_TS_ERR(-33); return 0; } else { LOG_DEBUG("PAT CRC_32 checked successfully"); } bs_free(b); return 1; }