📄 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., 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 stream_type);static AVStream* new_pes_av_stream(PESContext *pes, uint32_t code);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; 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) { tss->end_of_section_reached = 1; if (!tss->check_crc || av_crc(av_crc04C11DB7, -1, tss->section_buf, tss->section_h_size) == 0) tss->section_cb(tss->opaque, tss->section_buf, tss->section_h_size); }}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, 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 = (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; 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];#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(;;) { language[0] = 0; st = 0; stream_type = get8(&p, p_end); if (stream_type < 0) break; pid = get16(&p, p_end) & 0x1fff; if (pid < 0) break; desc_list_len = get16(&p, p_end) & 0xfff; if (desc_list_len < 0) break; desc_list_end = p + desc_list_len; if (desc_list_end > p_end) break; for(;;) { desc_tag = get8(&p, desc_list_end); if (desc_tag < 0) break; if (stream_type == STREAM_TYPE_PRIVATE_DATA && ((desc_tag == 0x6A) || (desc_tag == 0x7A))) { /*assume DVB AC-3 Audio*/ stream_type = STREAM_TYPE_AUDIO_AC3; } desc_len = get8(&p, desc_list_end); desc_end = p + desc_len; if (desc_end > desc_list_end) break;#ifdef DEBUG_SI printf("tag: 0x%02x len=%d\n", desc_tag, desc_len);#endif switch(desc_tag) { case DVB_SUBT_DESCID: if (stream_type == STREAM_TYPE_PRIVATE_DATA) stream_type = STREAM_TYPE_SUBTITLE_DVB; language[0] = get8(&p, desc_end); language[1] = get8(&p, desc_end); language[2] = get8(&p, desc_end); language[3] = 0; get8(&p, desc_end); comp_page = get16(&p, desc_end); anc_page = get16(&p, desc_end); break; case 0x0a: /* ISO 639 language descriptor */ language[0] = get8(&p, desc_end); language[1] = get8(&p, desc_end); language[2] = get8(&p, desc_end); language[3] = 0; break; default: break; } p = desc_end; } p = desc_list_end;#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: case STREAM_TYPE_AUDIO_DTS: case STREAM_TYPE_SUBTITLE_DVB: pes = add_pes_stream(ts, pid, stream_type); if (pes) st = new_pes_av_stream(pes, 0); break; default: /* we ignore the other streams */ break; } if (st) { if (language[0] != 0) { st->language[0] = language[0]; st->language[1] = language[1]; st->language[2] = language[2]; st->language[3] = language[3]; } if (stream_type == STREAM_TYPE_SUBTITLE_DVB) { st->codec->sub_id = (anc_page << 16) | comp_page; } } } /* all parameters are there */ ts->set_service_cb(ts->set_service_opaque, 0); mpegts_close_filter(ts, ts->pmt_filter); ts->pmt_filter = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -