📄 mpeg2_transport.c
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2002. All Rights Reserved. * * Contributor(s): * Bill May (wmay@cisco.com) *//* mpeg2_transport.c - mpeg2 transport stream decoding */#include "mpeg4ip.h"#include "mpeg2_transport.h"#include <assert.h>#include "mpeg2t_private.h"//#define DUMP_ADAPTION_CONTROL 1#define DEBUG 1#ifdef DEBUG#define CHECK_MP2T_HEADER assert(*pHdr == MPEG2T_SYNC_BYTE)#else#define CHECK_MP2T_HEADER #endif//#define DEBUG_MPEG2T 1/* * mpeg2 transport layer header routines. */uint32_t mpeg2t_find_sync_byte (const uint8_t *buffer, uint32_t buflen){ uint32_t offset; offset = 0; while (offset < buflen) { if (buffer[offset] == MPEG2T_SYNC_BYTE) { return (offset); } offset++; } return (offset);}uint32_t mpeg2t_transport_error_indicator (const uint8_t *pHdr){ CHECK_MP2T_HEADER; return ((pHdr[1] >> 7) & 0x1);}uint32_t mpeg2t_payload_unit_start_indicator (const uint8_t *pHdr){ CHECK_MP2T_HEADER; return ((pHdr[1] >> 6) & 0x1);}uint16_t mpeg2t_pid (const uint8_t *pHdr){ int pid; CHECK_MP2T_HEADER; pid = (pHdr[1] & 0x1f) << 8; pid |= pHdr[2]; return (pid);}uint32_t mpeg2t_adaptation_control (const uint8_t *pHdr){ CHECK_MP2T_HEADER; return ((pHdr[3] >> 4) & 0x3);}uint32_t mpeg2t_continuity_counter (const uint8_t *pHdr){ CHECK_MP2T_HEADER; return (pHdr[3] & 0xf);}/* * mpeg2t_transport_payload_start * find the start of the payload data, skipping any adaptation data */const uint8_t *mpeg2t_transport_payload_start (const uint8_t *pHdr, uint32_t *payload_len){ uint32_t adaption_control;#if DUMP_ADAPTION_CONTROL int ix, pid;#endif CHECK_MP2T_HEADER; if (mpeg2t_transport_error_indicator(pHdr) != 0) { *payload_len = 0; return NULL; } adaption_control = mpeg2t_adaptation_control(pHdr); if (adaption_control == 1) { *payload_len = 184; return pHdr + 4; } if (adaption_control == 3) { if (pHdr[4] > 183) { *payload_len = 0; return NULL; } *payload_len = 183 - pHdr[4];#if DUMP_ADAPTION_CONTROL pid = ((pHdr[1] << 8) | pHdr[2]) & 0x1fff; mpeg2t_message(LOG_ERR, "pid %x adaptation control - len %d", pid, pHdr[4]); if ((pHdr[5] & 0x10) != 0) { uint64_t pcr; uint16_t ext; pcr = pHdr[6]; pcr = (pcr << 8) | pHdr[7]; pcr = (pcr << 8) | pHdr[8]; pcr = (pcr << 8) | pHdr[9]; pcr <<= 1; if ((pHdr[10] & 0x80) != 0) { pcr |= 1; } ext = (pHdr[10] & 0x1) << 8; ext |= pHdr[11]; mpeg2t_message(LOG_ERR, "pid %x pcr "U64" ext %u", pid, pcr, ext); } for (ix = 0; ix < pHdr[4]; ix += 4) { mpeg2t_message(LOG_ERR, "pid %x %d - %02x %02x %02x %02x", pid, ix, pHdr[4 + ix], pHdr[5 + ix], pHdr[6 + ix], pHdr[7 + ix]); }#endif return pHdr + 5 + pHdr[4]; } *payload_len = 0; return NULL;}/* * mpeg2t_start_join_pak - this handles PMAP or PAS that are more * than 1 transport stream packet. It should also handle any other * type packet. * Elementary streams use their own processing routines. */static void mpeg2t_start_join_pak (mpeg2t_pid_t *pidptr, const uint8_t *bufstart, uint32_t buflen, uint32_t seqlen, uint32_t cc){ if (seqlen == 0) { if (pidptr->data_len_max < buflen) { pidptr->data = (uint8_t *)realloc(pidptr->data, buflen + 4096); if (pidptr->data == NULL) { pidptr->data_len_max = 0; return; } pidptr->data_len_max = buflen + 4096; } } else if (seqlen > pidptr->data_len_max) { pidptr->data = (uint8_t *)realloc(pidptr->data, seqlen); if (pidptr->data == NULL) { pidptr->data_len_max = 0; return; } pidptr->data_len_max = seqlen; } pidptr->data_len = seqlen; pidptr->data_len_loaded = buflen; memcpy(pidptr->data, bufstart, buflen); pidptr->lastcc = cc;}/* * mpeg2t_join_pak - add data to a started pak from above */static int mpeg2t_join_pak (mpeg2t_pid_t *pidptr, const uint8_t *bufstart, uint32_t buflen, uint32_t cc){ uint32_t nextcc; uint32_t remaining; if (pidptr->data_len_loaded == 0) { mpeg2t_message(LOG_WARNING, "Trying to add to unstarted packet - PID %x", pidptr->pid); return -1; } nextcc = (pidptr->lastcc + 1) & 0xf; if (nextcc != cc) { mpeg2t_message(LOG_ERR, "Illegal cc value %d - should be %d - PID %x", cc, nextcc, pidptr->pid); pidptr->data_len_loaded = 0; return -1; } pidptr->lastcc = cc; if (pidptr->data_len == 0) { remaining = pidptr->data_len_max - pidptr->data_len_loaded; if (remaining < buflen) { pidptr->data = (uint8_t *)realloc(pidptr->data, pidptr->data_len_max + 4096); if (pidptr->data == NULL) { pidptr->data_len_max = 0; return -1; } pidptr->data_len_max = pidptr->data_len_max + 4096; } } else { remaining = pidptr->data_len - pidptr->data_len_loaded; buflen = buflen > remaining ? remaining : buflen; } memcpy(pidptr->data + pidptr->data_len_loaded, bufstart, buflen); pidptr->data_len_loaded += buflen; if (pidptr->data_len == 0 || pidptr->data_len_loaded < pidptr->data_len) return 0; // Indicate that next one starts from beginning pidptr->data_len_loaded = 0; return 1;}/* * mpeg2t_lookup_pid - search through the list for the pid pointer */mpeg2t_pid_t *mpeg2t_lookup_pid (mpeg2t_t *ptr, uint16_t pid){ mpeg2t_pid_t *pidptr; SDL_LockMutex(ptr->pid_mutex); pidptr = &ptr->pas.pid; while (pidptr != NULL && pidptr->pid != pid) { pidptr = pidptr->next_pid; } SDL_UnlockMutex(ptr->pid_mutex); return pidptr;}/* * add_to_pidQ - add a PID to the queue */static void add_to_pidQ (mpeg2t_t *ptr, mpeg2t_pid_t *pidptr){ mpeg2t_pid_t *p = &ptr->pas.pid; pidptr->main = ptr; SDL_LockMutex(ptr->pid_mutex); while (p->next_pid != NULL) { p = p->next_pid; } p->next_pid = pidptr; SDL_UnlockMutex(ptr->pid_mutex);}/* * create_pmap - create a program map. This is called while processing * PAS statements. From here, we should get a PMAP packet for this * particular PID. That will give us the elementary streams that are * part of the PMAP (PMAP = program map) */static void create_pmap (mpeg2t_t *ptr, uint16_t prog_num, uint16_t pid){ mpeg2t_pmap_t *pmap; mpeg2t_message(LOG_NOTICE, "Adding pmap prog_num %x pid %x", prog_num, pid); pmap = MALLOC_STRUCTURE(mpeg2t_pmap_t); if (pmap == NULL) return; memset(pmap, 0, sizeof(*pmap)); pmap->pid.pak_type = MPEG2T_PROG_MAP_PAK; pmap->pid.pid = pid; pmap->program_number = prog_num; add_to_pidQ(ptr, &pmap->pid); ptr->program_count++;}/* * create_es - create elementary stream pid. This is called while * processing PMAPs. */static void create_es (mpeg2t_t *ptr, uint16_t pid, uint16_t prog_num, uint8_t stream_type, const uint8_t *es_data, uint32_t es_info_len){ mpeg2t_es_t *es; mpeg2t_message(LOG_INFO, "Adding ES PID %x stream type %d", pid, stream_type); es = MALLOC_STRUCTURE(mpeg2t_es_t); if (es == NULL) return; memset(es, 0, sizeof(*es)); es->list_mutex = SDL_CreateMutex(); if (es->list_mutex == NULL) { mpeg2t_message(LOG_ERR, "Can't malloc mutex"); free(es); return; } es->pid.pak_type = MPEG2T_ES_PAK; es->pid.pid = pid; es->prog_num = prog_num; es->stream_type = stream_type; switch (es->stream_type) { case 1: case 2: es->is_video = 1; break; case 0x10: es->is_video = 3; // mpeg4 break; case 0x1b: es->is_video = 2; break; default: es->is_video = 0; } if (es_info_len != 0) { es->es_data = (uint8_t *)malloc(es_info_len); if (es->es_data != NULL) { memcpy(es->es_data, es_data, es_info_len); es->es_info_len = es_info_len; } mpeg2t_message(LOG_ERR, "pid %x - es len %d %p", pid, es_info_len, es->es_data);#if 0 { uint32_t ix; for (ix = 0; ix < es_info_len; ix += 4) { mpeg2t_message(LOG_ERR, "%d - %02x %02x %02x %02x", ix, es_data[ix], es_data[ix + 1], es_data[ix + 2], es_data[ix + 3]); } }#endif } es->work_max_size = 4096; es->save_frames = ptr->save_frames_at_start; add_to_pidQ(ptr, &es->pid);} /* * mpeg2t_process_pas - process the PAS. The PAS is a list of PMAP * pids. */static int mpeg2t_process_pas (mpeg2t_t *ptr, const uint8_t *buffer){ uint32_t buflen; uint32_t section_len; uint32_t len; const uint8_t *mapptr; uint16_t prog_num, pid; const uint8_t *pasptr; int ret; buflen = 188; // process pas pointer pasptr = mpeg2t_transport_payload_start(buffer, &buflen); if (pasptr == NULL) return 0; if (mpeg2t_payload_unit_start_indicator(buffer) == 0) { ret = mpeg2t_join_pak(&ptr->pas.pid, pasptr, buflen, mpeg2t_continuity_counter(buffer)); if (ret <= 0) return 0; // not done, or bad pasptr = ptr->pas.pid.data; section_len = ptr->pas.pid.data_len; } else { if (*pasptr >= buflen) return 0; buflen -= *pasptr + 1; pasptr += *pasptr + 1; // go through the pointer field if (*pasptr != 0 || (pasptr[1] & 0xc0) != 0x80) { mpeg2t_message(LOG_ERR, "PAS field not 0"); return 0; } section_len = ((pasptr[1] << 8) | pasptr[2]) & 0x3ff; // remove table_id, section length fields pasptr += 3; buflen -= 3; if (buflen < section_len) { mpeg2t_start_join_pak(&ptr->pas.pid, pasptr, // start after section len buflen, section_len, mpeg2t_continuity_counter(buffer)); return 0; } // At this point, pasptr points to transport_stream_id } ptr->pas.transport_stream_id = ((pasptr[0] << 8 | pasptr[1])); ptr->pas.version_number = (pasptr[2] >> 1) & 0x1f; mapptr = &pasptr[5]; section_len -= 5 + 4; // remove CRC and stuff before map list /* * Now, we have a list of PMAPs - for each one, see if we've * already created a PIDPTR for it - if not, do so */ for (len = 0; len < section_len; len += 4, mapptr += 4) { prog_num = (mapptr[0] << 8) | mapptr[1]; if (prog_num != 0) { pid = ((mapptr[2] << 8) | mapptr[3]) & 0x1fff; if (mpeg2t_lookup_pid(ptr, pid) == NULL) { create_pmap(ptr, prog_num, pid); ptr->pas.programs++; // add to program count } } // else network information table } return 1;}/* * mpeg2t_process_pmap - process a PMAP, creating the elementary * stream PIDs */static int mpeg2t_process_pmap (mpeg2t_t *ptr, mpeg2t_pid_t *ifptr, const uint8_t *buffer){ uint32_t buflen; uint32_t section_len; uint32_t len, es_len; uint16_t prog_num, pcr_pid; const uint8_t *pmapptr; mpeg2t_pmap_t *pmap_pid = (mpeg2t_pmap_t *)ifptr; int ret; uint8_t stream_type; uint16_t e_pid; uint32_t ix; buflen = 188; // process pas pointer pmapptr = mpeg2t_transport_payload_start(buffer, &buflen); if (pmapptr == NULL) return 0; if (mpeg2t_payload_unit_start_indicator(buffer) == 0) { ret = mpeg2t_join_pak(ifptr, pmapptr, buflen, mpeg2t_continuity_counter(buffer)); if (ret <= 0) return 0; pmapptr = ifptr->data; section_len = ifptr->data_len; } else { if (*pmapptr >= buflen) return 0; buflen -= *pmapptr + 1; pmapptr += *pmapptr + 1; // go through the pointer field if (*pmapptr != 2 || (pmapptr[1] & 0xc0) != 0x80) { mpeg2t_message(LOG_ERR, "PMAP start field not 2"); return 0; } section_len = ((pmapptr[1] << 8) | pmapptr[2]) & 0x3ff; pmapptr += 3; buflen -= 3; if (buflen < section_len) { mpeg2t_start_join_pak(ifptr, pmapptr, buflen, section_len, mpeg2t_continuity_counter(buffer)); return 0; } // pmapptr points to program number } prog_num = ((pmapptr[0] << 8) | pmapptr[1]);#if 0 if (prog_num != pmap_pid->program_number) { mpeg2t_message(LOG_ERR, "Prog Map error - program number doesn't match - pid %x orig %x from pak %x", pmap_pid->pid.pid, pmap_pid->program_number, prog_num); return 0; }#endif pmap_pid->version_number = (pmapptr[2] >> 1) & 0x1f; pcr_pid = ((pmapptr[5] << 8) | pmapptr[6]) & 0x1fff; if (pcr_pid != 0x1fff) { mpeg2t_message(LOG_DEBUG, "Have PCR pid of %x", pcr_pid); } pmapptr += 7; section_len -= 7; // remove all the fixed fields to get the prog info len len = ((pmapptr[0] << 8) | pmapptr[1]) & 0xfff; pmapptr += 2; section_len -= 2; if (len != 0) { // This is the prog info - we don't do anything with it yet. if (len > section_len) return 0; mpeg2t_message(LOG_ERR, "have prog info len %d", len); for (ix = 0; ix < len; ix += 4) { mpeg2t_message(LOG_ERR, "%d - %02x %02x %02x %02x", ix, pmapptr[ix], pmapptr[ix + 1], pmapptr[ix + 2], pmapptr[ix + 3]); } if (len == pmap_pid->prog_info_len) { // same length - do a compare } else { } pmapptr += len; section_len -= len; } section_len -= 4; // remove CRC len = 0; if (section_len == 0) { return 0; } /* * Now, add all elementary streams for this PMAP */ if (pmap_pid->received == 0) { pmap_pid->received = 1; ptr->program_maps_recvd++; ptr->pas.programs_added++; } while (len < section_len) { stream_type = pmapptr[0]; e_pid = ((pmapptr[1] << 8) | pmapptr[2]) & 0x1fff; es_len = ((pmapptr[3] << 8) | pmapptr[4]) & 0xfff; if (es_len + len > section_len) return 0; if (mpeg2t_lookup_pid(ptr, e_pid) == NULL) { mpeg2t_message(LOG_NOTICE, "Creating es pid %x", e_pid); create_es(ptr, e_pid, pmap_pid->program_number, stream_type, &pmapptr[5], es_len); } // create_es len += 5 + es_len; pmapptr += 5 + es_len; } return 1;}/* * clean_es_data * clean all data needed for processing the stream. This * should really be broken out into other files, like the * frame processing. */static void clean_es_data (mpeg2t_es_t *es_pid) { es_pid->have_ps_ts = 0; es_pid->have_dts = 0; if (es_pid->is_video > 0) { // mpeg1 or mpeg2 video es_pid->work_state = 0; es_pid->header = 0; es_pid->work_loaded = 0; es_pid->have_seq_header = 0; if (es_pid->work != NULL) { mpeg2t_malloc_es_work(es_pid, es_pid->work->frame_len); } } else { // mpeg1/mpeg2 audio (mp3 codec) if (es_pid->work != NULL ) { free(es_pid->work); es_pid->work = NULL; } es_pid->work_loaded = 0; es_pid->left = 0; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -