📄 mpegts.c
字号:
/* * MPEG2 transport stream (aka DVB) demux * Copyright (c) 2002-2003 Fabrice Bellard. * * This library 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 of the License, or (at your option) any later version. * * This library 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; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include "avformat.h"#include "mpegts.h"//#define DEBUG_SI//#define DEBUG_SEEK/* 1.0 second at 24Mbit/s */#define MAX_SCAN_PACKETS 32000/* maximum size in which we look for synchronisation if synchronisation is lost */#define MAX_RESYNC_SIZE 4096static int add_pes_stream(MpegTSContext *ts, int pid, int stream_type);enum MpegTSFilterType { MPEGTS_PES, MPEGTS_SECTION,};typedef void PESCallback(void *opaque, const uint8_t *buf, int len, int is_start);typedef struct MpegTSPESFilter { PESCallback *pes_cb; void *opaque;} MpegTSPESFilter;typedef void SectionCallback(void *opaque, const uint8_t *buf, int len);typedef void SetServiceCallback(void *opaque, int ret);typedef struct MpegTSSectionFilter { int section_index; int section_h_size; uint8_t *section_buf; int check_crc:1; int end_of_section_reached:1; SectionCallback *section_cb; void *opaque;} MpegTSSectionFilter;typedef struct MpegTSFilter { int pid; int last_cc; /* last cc code (-1 if first packet) */ enum MpegTSFilterType type; union { MpegTSPESFilter pes_filter; MpegTSSectionFilter section_filter; } u;} MpegTSFilter;typedef struct MpegTSService { int running:1; int sid; char *provider_name; char *name;} MpegTSService;struct MpegTSContext { /* user data */ AVFormatContext *stream; int raw_packet_size; /* raw packet size, including FEC if present */ int auto_guess; /* if true, all pids are analized to find streams */ int set_service_ret; int mpeg2ts_raw; /* force raw MPEG2 transport stream output, if possible */ int mpeg2ts_compute_pcr; /* compute exact PCR for each transport stream packet */ /* used to estimate the exact PCR */ int64_t cur_pcr; int pcr_incr; int pcr_pid; /* data needed to handle file based ts */ int stop_parse; /* stop parsing loop */ AVPacket *pkt; /* packet containing av data */ /******************************************/ /* private mpegts data */ /* scan context */ MpegTSFilter *sdt_filter; int nb_services; MpegTSService **services; /* set service context (XXX: allocated it ?) */ SetServiceCallback *set_service_cb; void *set_service_opaque; MpegTSFilter *pat_filter; MpegTSFilter *pmt_filter; int req_sid; MpegTSFilter *pids[NB_PID_MAX];};static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, const uint8_t *buf, int buf_size, int is_start){ MpegTSSectionFilter *tss = &tss1->u.section_filter; int len; unsigned int crc; if (is_start) { memcpy(tss->section_buf, buf, buf_size); tss->section_index = buf_size; tss->section_h_size = -1; tss->end_of_section_reached = 0; } else { if (tss->end_of_section_reached) return; len = 4096 - tss->section_index; if (buf_size < len) len = buf_size; memcpy(tss->section_buf + tss->section_index, buf, len); tss->section_index += len; } /* compute section length if possible */ if (tss->section_h_size == -1 && tss->section_index >= 3) { len = (((tss->section_buf[1] & 0xf) << 8) | tss->section_buf[2]) + 3; if (len > 4096) return; tss->section_h_size = len; } if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { if (tss->check_crc) { crc = mpegts_crc32(tss->section_buf, tss->section_h_size); if (crc != 0) goto invalid_crc; } tss->section_cb(tss->opaque, tss->section_buf, tss->section_h_size); invalid_crc: tss->end_of_section_reached = 1; }}MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid, SectionCallback *section_cb, void *opaque, int check_crc){ MpegTSFilter *filter; MpegTSSectionFilter *sec; #ifdef DEBUG_SI printf("Filter: pid=0x%x\n", pid);#endif if (pid >= NB_PID_MAX || ts->pids[pid]) return NULL; filter = av_mallocz(sizeof(MpegTSFilter)); if (!filter) return NULL; ts->pids[pid] = filter; filter->type = MPEGTS_SECTION; filter->pid = pid; filter->last_cc = -1; sec = &filter->u.section_filter; sec->section_cb = section_cb; sec->opaque = opaque; sec->section_buf = av_malloc(MAX_SECTION_SIZE); sec->check_crc = check_crc; if (!sec->section_buf) { av_free(filter); return NULL; } return filter;}MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid, PESCallback *pes_cb, void *opaque){ MpegTSFilter *filter; MpegTSPESFilter *pes; if (pid >= NB_PID_MAX || ts->pids[pid]) return NULL; filter = av_mallocz(sizeof(MpegTSFilter)); if (!filter) return NULL; ts->pids[pid] = filter; filter->type = MPEGTS_PES; filter->pid = pid; filter->last_cc = -1; pes = &filter->u.pes_filter; pes->pes_cb = pes_cb; pes->opaque = opaque; return filter;}void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter){ int pid; pid = filter->pid; if (filter->type == MPEGTS_SECTION) av_freep(&filter->u.section_filter.section_buf); else if (filter->type == MPEGTS_PES) av_freep(&filter->u.pes_filter.opaque); av_free(filter); ts->pids[pid] = NULL;}static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ int stat[packet_size]; int i; int x=0; int best_score=0; memset(stat, 0, packet_size*sizeof(int)); for(x=i=0; i<size; i++){ if(buf[i] == 0x47){ stat[x]++; if(stat[x] > best_score){ best_score= stat[x]; if(index) *index= x; } } x++; if(x == packet_size) x= 0; } return best_score;}/* autodetect fec presence. Must have at least 1024 bytes */static int get_packet_size(const uint8_t *buf, int size){ int score, fec_score; if (size < (TS_FEC_PACKET_SIZE * 5 + 1)) return -1; score = analyze(buf, size, TS_PACKET_SIZE, NULL); fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL);// av_log(NULL, AV_LOG_DEBUG, "score: %d, fec_score: %d \n", score, fec_score); if (score > fec_score) return TS_PACKET_SIZE; else if(score < fec_score) return TS_FEC_PACKET_SIZE; else return -1;}typedef struct SectionHeader { uint8_t tid; uint16_t id; uint8_t version; uint8_t sec_num; uint8_t last_sec_num;} SectionHeader;static inline int get8(const uint8_t **pp, const uint8_t *p_end){ const uint8_t *p; int c; p = *pp; if (p >= p_end) return -1; c = *p++; *pp = p; return c;}static inline int get16(const uint8_t **pp, const uint8_t *p_end){ const uint8_t *p; int c; p = *pp; if ((p + 1) >= p_end) return -1; c = (p[0] << 8) | p[1]; p += 2; *pp = p; return c;}/* read and allocate a DVB string preceeded by its length */static char *getstr8(const uint8_t **pp, const uint8_t *p_end){ int len; const uint8_t *p; char *str; p = *pp; len = get8(&p, p_end); if (len < 0) return NULL; if ((p + len) > p_end) return NULL; str = av_malloc(len + 1); if (!str) return NULL; memcpy(str, p, len); str[len] = '\0'; p += len; *pp = p; return str;}static int parse_section_header(SectionHeader *h, const uint8_t **pp, const uint8_t *p_end){ int val; val = get8(pp, p_end); if (val < 0) return -1; h->tid = val; *pp += 2; val = get16(pp, p_end); if (val < 0) return -1; h->id = val; val = get8(pp, p_end); if (val < 0) return -1; h->version = (val >> 1) & 0x1f; val = get8(pp, p_end); if (val < 0) return -1; h->sec_num = val; val = get8(pp, p_end); if (val < 0) return -1; h->last_sec_num = val; return 0;}static MpegTSService *new_service(MpegTSContext *ts, int sid, char *provider_name, char *name){ MpegTSService *service;#ifdef DEBUG_SI printf("new_service: sid=0x%04x provider='%s' name='%s'\n", sid, provider_name, name);#endif service = av_mallocz(sizeof(MpegTSService)); if (!service) return NULL; service->sid = sid; service->provider_name = provider_name; service->name = name; dynarray_add(&ts->services, &ts->nb_services, service); return service;}static void pmt_cb(void *opaque, const uint8_t *section, int section_len){ MpegTSContext *ts = opaque; SectionHeader h1, *h = &h1; const uint8_t *p, *p_end; int program_info_length, pcr_pid, pid, stream_type, desc_length; #ifdef DEBUG_SI printf("PMT:\n"); av_hex_dump(stdout, (uint8_t *)section, section_len);#endif p_end = section + section_len - 4; p = section; if (parse_section_header(h, &p, p_end) < 0) return;#ifdef DEBUG_SI printf("sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num);#endif if (h->tid != PMT_TID || (ts->req_sid >= 0 && h->id != ts->req_sid) ) return; pcr_pid = get16(&p, p_end) & 0x1fff; if (pcr_pid < 0) return; ts->pcr_pid = pcr_pid;#ifdef DEBUG_SI printf("pcr_pid=0x%x\n", pcr_pid);#endif program_info_length = get16(&p, p_end) & 0xfff; if (program_info_length < 0) return; p += program_info_length; if (p >= p_end) return; for(;;) { stream_type = get8(&p, p_end); if (stream_type < 0) break; pid = get16(&p, p_end) & 0x1fff; if (pid < 0) break; desc_length = get16(&p, p_end) & 0xfff; if (desc_length < 0) break; p += desc_length; if (p > p_end) return;#ifdef DEBUG_SI printf("stream_type=%d pid=0x%x\n", stream_type, pid);#endif /* now create ffmpeg stream */ switch(stream_type) { case STREAM_TYPE_AUDIO_MPEG1: case STREAM_TYPE_AUDIO_MPEG2: case STREAM_TYPE_VIDEO_MPEG1: case STREAM_TYPE_VIDEO_MPEG2: case STREAM_TYPE_VIDEO_MPEG4: case STREAM_TYPE_VIDEO_H264: case STREAM_TYPE_AUDIO_AAC: case STREAM_TYPE_AUDIO_AC3: add_pes_stream(ts, pid, stream_type); break; default: /* we ignore the other streams */ break; } } /* all parameters are there */ ts->set_service_cb(ts->set_service_opaque, 0); mpegts_close_filter(ts, ts->pmt_filter); ts->pmt_filter = NULL;}static void pat_cb(void *opaque, const uint8_t *section, int section_len){ MpegTSContext *ts = opaque; SectionHeader h1, *h = &h1; const uint8_t *p, *p_end; int sid, pmt_pid;#ifdef DEBUG_SI printf("PAT:\n"); av_hex_dump(stdout, (uint8_t *)section, section_len);#endif p_end = section + section_len - 4; p = section; if (parse_section_header(h, &p, p_end) < 0) return; if (h->tid != PAT_TID) return; for(;;) { sid = get16(&p, p_end); if (sid < 0) break; pmt_pid = get16(&p, p_end) & 0x1fff; if (pmt_pid < 0) break;#ifdef DEBUG_SI printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);#endif if (sid == 0x0000) { /* NIT info */ } else { if (ts->req_sid == sid) { ts->pmt_filter = mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1); goto found; } } } /* not found */ ts->set_service_cb(ts->set_service_opaque, -1); found: mpegts_close_filter(ts, ts->pat_filter); ts->pat_filter = NULL;}/* add all services found in the PAT */static void pat_scan_cb(void *opaque, const uint8_t *section, int section_len){ MpegTSContext *ts = opaque; SectionHeader h1, *h = &h1; const uint8_t *p, *p_end; int sid, pmt_pid; char *provider_name, *name; char buf[256];#ifdef DEBUG_SI printf("PAT:\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -