📄 mpegts.c.svn-base
字号:
/* * MPEG2 transport stream (aka DVB) demuxer * Copyright (c) 2002-2003 Fabrice Bellard. * * This file is part of FFmpeg. * * FFmpeg 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.1 of the License, or (at your option) any later version. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */#include "avformat.h"#include "crc.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 4096typedef struct PESContext PESContext;static PESContext* add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid, int stream_type);static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code);extern void av_set_program_name(AVProgram *program, char *provider_name, char *name);extern void av_program_add_stream_index(AVFormatContext *ac, int progid, unsigned int idx);enum MpegTSFilterType { MPEGTS_PES, MPEGTS_SECTION,};typedef struct MpegTSFilter MpegTSFilter;typedef void PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start);typedef struct MpegTSPESFilter { PESCallback *pes_cb; void *opaque;} MpegTSPESFilter;typedef void SectionCallback(MpegTSFilter *f, 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;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;};#define MAX_PIDS_PER_PROGRAM 64typedef struct { unsigned int id; //program id/service id unsigned int nb_pids; unsigned int pids[MAX_PIDS_PER_PROGRAM];} Program_t;struct MpegTSContext { /* user data */ AVFormatContext *stream; /** raw packet size, including FEC if present */ int raw_packet_size; /** if true, all pids are analyzed to find streams */ int auto_guess; /** compute exact PCR for each transport stream packet */ int mpeg2ts_compute_pcr; int64_t cur_pcr; /**< used to estimate the exact PCR */ int pcr_incr; /**< used to estimate the exact PCR */ /* data needed to handle file based ts */ /** stop parsing loop */ int stop_parse; /** packet containing Audio/Video data */ AVPacket *pkt; /******************************************/ /* private mpegts data */ /* scan context */ /** structure to keep track of Program->pids mapping */ unsigned int nb_prg; Program_t *prg; /** filters for various streams specified by PMT + for the PAT and PMT */ MpegTSFilter *pids[NB_PID_MAX];};/* TS stream handling */enum MpegTSState { MPEGTS_HEADER = 0, MPEGTS_PESHEADER_FILL, MPEGTS_PAYLOAD, MPEGTS_SKIP,};/* enough for PES header + length */#define PES_START_SIZE 9#define MAX_PES_HEADER_SIZE (9 + 255)struct PESContext { int pid; int pcr_pid; /**< if -1 then all packets containing PCR are considered */ int stream_type; MpegTSContext *ts; AVFormatContext *stream; AVStream *st; enum MpegTSState state; /* used to get the format */ int data_index; int total_size; int pes_header_size; int64_t pts, dts; uint8_t header[MAX_PES_HEADER_SIZE];};extern AVInputFormat mpegts_demuxer;static void clear_program(MpegTSContext *ts, unsigned int programid){ int i; for(i=0; i<ts->nb_prg; i++) if(ts->prg[i].id == programid) ts->prg[i].nb_pids = 0;}static void clear_programs(MpegTSContext *ts){ av_freep(&ts->prg); ts->nb_prg=0;}static void add_pat_entry(MpegTSContext *ts, unsigned int programid){ Program_t *p; void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(Program_t)); if(!tmp) return; ts->prg = tmp; p = &ts->prg[ts->nb_prg]; p->id = programid; p->nb_pids = 0; ts->nb_prg++;}static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid){ int i; Program_t *p = NULL; for(i=0; i<ts->nb_prg; i++) { if(ts->prg[i].id == programid) { p = &ts->prg[i]; break; } } if(!p) return; if(p->nb_pids >= MAX_PIDS_PER_PROGRAM) return; p->pids[p->nb_pids++] = pid;}/** * \brief discard_pid() decides if the pid is to be discarded according * to caller's programs selection * \param ts : - TS context * \param pid : - pid * \return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL * 0 otherwise */static int discard_pid(MpegTSContext *ts, unsigned int pid){ int i, j, k; int used = 0, discarded = 0; Program_t *p; for(i=0; i<ts->nb_prg; i++) { p = &ts->prg[i]; for(j=0; j<p->nb_pids; j++) { if(p->pids[j] != pid) continue; //is program with id p->id set to be discarded? for(k=0; k<ts->stream->nb_programs; k++) { if(ts->stream->programs[k]->id == p->id) { if(ts->stream->programs[k]->discard == AVDISCARD_ALL) discarded++; else used++; } } } } return (!used && discarded);}/** * Assembles PES packets out of TS packets, and then calls the "section_cb" * function when they are complete. */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; 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 = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3; if (len > 4096) return; tss->section_h_size = len; } if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) { tss->end_of_section_reached = 1; if (!tss->check_crc || av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, tss->section_buf, tss->section_h_size) == 0) tss->section_cb(tss1, tss->section_buf, tss->section_h_size); }}static 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 av_log(ts->stream, AV_LOG_DEBUG, "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;}static 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;}static 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, dvhs_score; if (size < (TS_FEC_PACKET_SIZE * 5 + 1)) return -1; score = analyze(buf, size, TS_PACKET_SIZE, NULL); dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL); fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL);// av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score); if (score > fec_score && score > dvhs_score) return TS_PACKET_SIZE; else if(dvhs_score > score && dvhs_score > fec_score) return TS_DVHS_PACKET_SIZE; else if(score < fec_score && dvhs_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 = AV_RB16(p); 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 void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len){ MpegTSContext *ts = filter->u.section_filter.opaque; SectionHeader h1, *h = &h1; PESContext *pes; AVStream *st; const uint8_t *p, *p_end, *desc_list_end, *desc_end; int program_info_length, pcr_pid, pid, stream_type; int desc_list_len, desc_len, desc_tag; int comp_page = 0, anc_page = 0; /* initialize to kill warnings */ char language[4] = {0}; /* initialize to kill warnings */#ifdef DEBUG_SI av_log(ts->stream, AV_LOG_DEBUG, "PMT: len %i\n", section_len); av_hex_dump_log(ts->stream, AV_LOG_DEBUG, (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 av_log(ts->stream, AV_LOG_DEBUG, "sid=0x%x sec_num=%d/%d\n", h->id, h->sec_num, h->last_sec_num);#endif if (h->tid != PMT_TID) return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -