/*
- * MPEG2 transport stream (aka DVB) demux
+ * MPEG2 transport stream (aka DVB) demuxer
* Copyright (c) 2002-2003 Fabrice Bellard.
*
- * This library is free software; you can redistribute it and/or
+ * 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 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
- * This library is distributed in the hope that it will be useful,
+ * 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 this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 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
typedef struct MpegTSService {
int running:1;
- int sid;
- char *provider_name;
- char *name;
+ int sid; /**< MPEG Program Number of stream */
+ char *provider_name; /**< DVB Network name, "" if not DVB stream */
+ char *name; /**< DVB Service name, "MPEG Program [sid]" if not DVB stream*/
} 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 */
+ /** raw packet size, including FEC if present */
+ int raw_packet_size;
+ /** if true, all pids are analyzed to find streams */
+ int auto_guess;
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 */
+ /** force raw MPEG2 transport stream output, if possible */
+ int mpeg2ts_raw;
+ /** compute exact PCR for each transport stream packet */
+ int mpeg2ts_compute_pcr;
- /* used to estimate the exact PCR */
- int64_t cur_pcr;
- int pcr_incr;
- int pcr_pid;
+ int64_t cur_pcr; /**< used to estimate the exact PCR */
+ int pcr_incr; /**< used to estimate the exact PCR */
+ int pcr_pid; /**< used to estimate the exact PCR */
/* data needed to handle file based ts */
- int stop_parse; /* stop parsing loop */
- AVPacket *pkt; /* packet containing av data */
+ /** stop parsing loop */
+ int stop_parse;
+ /** packet containing Audio/Video data */
+ AVPacket *pkt;
/******************************************/
/* private mpegts data */
/* scan context */
MpegTSFilter *sdt_filter;
+ /** number of PMTs in the last PAT seen */
int nb_services;
+ /** list of PMTs in the last PAT seen */
MpegTSService **services;
/* set service context (XXX: allocated it ?) */
SetServiceCallback *set_service_cb;
void *set_service_opaque;
+ /** filter for the PAT */
MpegTSFilter *pat_filter;
+ /** filter for the PMT for the MPEG program number specified by req_sid */
MpegTSFilter *pmt_filter;
+ /** MPEG program number of stream we want to decode */
int req_sid;
+ /** filters for various streams specified by PMT + for the PAT and PMT */
MpegTSFilter *pids[NB_PID_MAX];
};
+/**
+ * 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)
{
if (tss->section_h_size != -1 && tss->section_index >= tss->section_h_size) {
tss->end_of_section_reached = 1;
if (!tss->check_crc ||
- mpegts_crc32(tss->section_buf, tss->section_h_size) == 0)
+ 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,
+static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid,
SectionCallback *section_cb, void *opaque,
int check_crc)
MpegTSSectionFilter *sec;
#ifdef DEBUG_SI
- printf("Filter: pid=0x%x\n", pid);
+ av_log(NULL, AV_LOG_DEBUG, "Filter: pid=0x%x\n", pid);
#endif
if (pid >= NB_PID_MAX || ts->pids[pid])
return NULL;
return filter;
}
-MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid,
+static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid,
PESCallback *pes_cb,
void *opaque)
{
return filter;
}
-void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
+static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter)
{
int pid;
/* autodetect fec presence. Must have at least 1024 bytes */
static int get_packet_size(const uint8_t *buf, int size)
{
- int score, fec_score;
+ 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, fec_score: %d \n", score, fec_score);
+// av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
- if (score > fec_score) return TS_PACKET_SIZE;
- else if(score < fec_score) return TS_FEC_PACKET_SIZE;
+ 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;
}
MpegTSService *service;
#ifdef DEBUG_SI
- printf("new_service: sid=0x%04x provider='%s' name='%s'\n",
+ av_log(NULL, AV_LOG_DEBUG, "new_service: "
+ "sid=0x%04x provider='%s' name='%s'\n",
sid, provider_name, name);
#endif
char language[4];
#ifdef DEBUG_SI
- printf("PMT:\n");
+ av_log(NULL, AV_LOG_DEBUG, "PMT: len %i\n", section_len);
av_hex_dump(stdout, (uint8_t *)section, section_len);
#endif
p_end = section + section_len - 4;
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);
+ av_log(NULL, 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 || (ts->req_sid >= 0 && h->id != ts->req_sid) )
return;
return;
ts->pcr_pid = pcr_pid;
#ifdef DEBUG_SI
- printf("pcr_pid=0x%x\n", pcr_pid);
+ av_log(NULL, AV_LOG_DEBUG, "pcr_pid=0x%x\n", pcr_pid);
#endif
program_info_length = get16(&p, p_end) & 0xfff;
if (program_info_length < 0)
desc_tag = get8(&p, desc_list_end);
if (desc_tag < 0)
break;
+ if (stream_type == STREAM_TYPE_PRIVATE_DATA) {
+ if((desc_tag == 0x6A) || (desc_tag == 0x7A)) {
+ /*assume DVB AC-3 Audio*/
+ stream_type = STREAM_TYPE_AUDIO_AC3;
+ } else if(desc_tag == 0x7B) {
+ /* DVB DTS audio */
+ stream_type = STREAM_TYPE_AUDIO_DTS;
+ }
+ }
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);
+ av_log(NULL, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n",
+ desc_tag, desc_len);
#endif
switch(desc_tag) {
case DVB_SUBT_DESCID:
p = desc_list_end;
#ifdef DEBUG_SI
- printf("stream_type=%d pid=0x%x\n", stream_type, pid);
+ av_log(NULL, AV_LOG_DEBUG, "stream_type=%d pid=0x%x\n",
+ stream_type, pid);
#endif
/* now create ffmpeg stream */
int sid, pmt_pid;
#ifdef DEBUG_SI
- printf("PAT:\n");
+ av_log(NULL, AV_LOG_DEBUG, "PAT:\n");
av_hex_dump(stdout, (uint8_t *)section, section_len);
#endif
p_end = section + section_len - 4;
if (pmt_pid < 0)
break;
#ifdef DEBUG_SI
- printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
+ av_log(NULL, AV_LOG_DEBUG, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
#endif
if (sid == 0x0000) {
/* NIT info */
char buf[256];
#ifdef DEBUG_SI
- printf("PAT:\n");
+ av_log(NULL, AV_LOG_DEBUG, "PAT:\n");
av_hex_dump(stdout, (uint8_t *)section, section_len);
#endif
p_end = section + section_len - 4;
if (pmt_pid < 0)
break;
#ifdef DEBUG_SI
- printf("sid=0x%x pid=0x%x\n", sid, pmt_pid);
+ av_log(NULL, AV_LOG_DEBUG, "sid=0x%x pid=0x%x\n", sid, pmt_pid);
#endif
if (sid == 0x0000) {
/* NIT info */
ts->pat_filter = NULL;
}
-void mpegts_set_service(MpegTSContext *ts, int sid,
+static void mpegts_set_service(MpegTSContext *ts, int sid,
SetServiceCallback *set_service_cb, void *opaque)
{
ts->set_service_cb = set_service_cb;
char *name, *provider_name;
#ifdef DEBUG_SI
- printf("SDT:\n");
+ av_log(NULL, AV_LOG_DEBUG, "SDT:\n");
av_hex_dump(stdout, (uint8_t *)section, section_len);
#endif
if (desc_end > desc_list_end)
break;
#ifdef DEBUG_SI
- printf("tag: 0x%02x len=%d\n", desc_tag, desc_len);
+ av_log(NULL, AV_LOG_DEBUG, "tag: 0x%02x len=%d\n",
+ desc_tag, desc_len);
#endif
switch(desc_tag) {
case 0x48:
}
/* scan services in a transport stream by looking at the SDT */
-void mpegts_scan_sdt(MpegTSContext *ts)
+static void mpegts_scan_sdt(MpegTSContext *ts)
{
ts->sdt_filter = mpegts_open_section_filter(ts, SDT_PID,
sdt_cb, ts, 1);
/* scan services in a transport stream by looking at the PAT (better
than nothing !) */
-void mpegts_scan_pat(MpegTSContext *ts)
+static void mpegts_scan_pat(MpegTSContext *ts)
{
ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID,
pat_scan_cb, ts, 1);
{
#if 1
const int size= p->buf_size;
- int score, fec_score;
+ int score, fec_score, dvhs_score;
#define CHECK_COUNT 10
if (size < (TS_FEC_PACKET_SIZE * CHECK_COUNT))
return -1;
score = analyze(p->buf, TS_PACKET_SIZE *CHECK_COUNT, TS_PACKET_SIZE, NULL);
+ dvhs_score = analyze(p->buf, TS_DVHS_PACKET_SIZE *CHECK_COUNT, TS_DVHS_PACKET_SIZE, NULL);
fec_score= analyze(p->buf, TS_FEC_PACKET_SIZE*CHECK_COUNT, TS_FEC_PACKET_SIZE, NULL);
-// av_log(NULL, AV_LOG_DEBUG, "score: %d, fec_score: %d \n", score, fec_score);
+// av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score);
// we need a clear definition for the returned score otherwise things will become messy sooner or later
- if (score > fec_score && score > 6) return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
+ if (score > fec_score && score > dvhs_score && score > 6) return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
+ else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
else if( fec_score > 6) return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
else return -1;
#else
#endif
}
-void set_service_cb(void *opaque, int ret)
+static void set_service_cb(void *opaque, int ret)
{
MpegTSContext *ts = opaque;
ts->set_service_ret = ret;
ts->stream = s;
ts->auto_guess = 0;
+goto_auto_guess:
if (!ts->mpeg2ts_raw) {
/* normal demux */
url_fseek(pb, pos, SEEK_SET);
mpegts_scan_sdt(ts);
- handle_packets(ts, MAX_SCAN_PACKETS);
+ handle_packets(ts, s->probesize);
if (ts->nb_services <= 0) {
/* no SDT found, we try to look at the PAT */
url_fseek(pb, pos, SEEK_SET);
mpegts_scan_pat(ts);
- handle_packets(ts, MAX_SCAN_PACKETS);
+ handle_packets(ts, s->probesize);
}
if (ts->nb_services <= 0) {
service = ts->services[i];
sid = service->sid;
#ifdef DEBUG_SI
- printf("tuning to '%s'\n", service->name);
+ av_log(NULL, AV_LOG_DEBUG, "tuning to '%s'\n", service->name);
#endif
/* now find the info for the first service if we found any,
url_fseek(pb, pos, SEEK_SET);
mpegts_set_service(ts, sid, set_service_cb, ts);
- handle_packets(ts, MAX_SCAN_PACKETS);
+ handle_packets(ts, s->probesize);
}
/* if could not find service, exit */
- if (ts->set_service_ret != 0)
- return -1;
+ if (ts->set_service_ret != 0) {
+ if(ts->auto_guess)
+ return -1;
+ else {
+ //let's retry with auto_guess set
+ ts->auto_guess = 1;
+ goto goto_auto_guess;
+ }
+ }
#ifdef DEBUG_SI
- printf("tuning done\n");
+ av_log(NULL, AV_LOG_DEBUG, "tuning done\n");
#endif
}
s->ctx_flags |= AVFMTCTX_NOHEADER;
st->codec->bit_rate = s->bit_rate;
st->start_time = ts->cur_pcr;
#if 0
- printf("start=%0.3f pcr=%0.3f incr=%d\n",
+ av_log(NULL, AV_LOG_DEBUG, "start=%0.3f pcr=%0.3f incr=%d\n",
st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr);
#endif
}
int i;
for(i=0;i<NB_PID_MAX;i++)
if (ts->pids[i]) mpegts_close_filter(ts, ts->pids[i]);
+
+ for(i = 0; i < ts->nb_services; i++){
+ av_free(ts->services[i]->provider_name);
+ av_free(ts->services[i]->name);
+ av_free(ts->services[i]);
+ }
+ av_freep(&ts->services);
+
return 0;
}
av_free(ts);
}
-AVInputFormat mpegts_demux = {
+AVInputFormat mpegts_demuxer = {
"mpegts",
"MPEG2 transport stream format",
sizeof(MpegTSContext),
mpegts_get_pcr,
.flags = AVFMT_SHOW_IDS,
};
-
-int mpegts_init(void)
-{
- av_register_input_format(&mpegts_demux);
-#ifdef CONFIG_MUXERS
- av_register_output_format(&mpegts_mux);
-#endif
- return 0;
-}