[FFmpeg-devel] mpeg TS encoder PCR algo + looping
ffmpeg at a.legko.ru
ffmpeg at a.legko.ru
Wed Mar 29 15:12:02 EEST 2017
Hi!
Thanx for help with git!
Reposting the same patch in git format now.
-------------- next part --------------
From 248d67b353cd76f201b7174a6bdf160e6c6f64b1 Mon Sep 17 00:00:00 2001
From: Vadka aka VVS <ffmpeg at a.legko.ru>
Date: Wed, 29 Mar 2017 15:09:32 +0300
Subject: [PATCH] ffmpeg.c - bug with streams loop/repeat fixed; ffmpeg_opt.c -
adding missed options for libavformat/mpegtsenc.c; libavformat/mpegtsenc.c -
PCR pids selection procedure changed, making it possible to broadcast
standard mpeg TS/DVB streams with multiple programs (it is possible to define
pcr_pid manually now).
---
ffmpeg.c | 26 ++++++++++
ffmpeg_opt.c | 7 ++-
libavformat/mpegtsenc.c | 131 ++++++++++++++++++++++++++++++++----------------
3 files changed, 120 insertions(+), 44 deletions(-)
diff --git a/ffmpeg.c b/ffmpeg.c
index 3aa1e78..254960f 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -4028,6 +4028,31 @@ static int init_input_threads(void)
return 0;
}
+static int init_input_thread(int i)
+{
+ int ret;
+
+ if (nb_input_files == 1)
+ return 0;
+
+ InputFile *f = input_files[i];
+
+ if (f->ctx->pb ? !f->ctx->pb->seekable :
+ strcmp(f->ctx->iformat->name, "lavfi"))
+ f->non_blocking = 1;
+ ret = av_thread_message_queue_alloc(&f->in_thread_queue,
+ f->thread_queue_size, sizeof(AVPacket));
+ if (ret < 0)
+ return ret;
+
+ if ((ret = pthread_create(&f->thread, NULL, input_thread, f))) {
+ av_log(NULL, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret));
+ av_thread_message_queue_free(&f->in_thread_queue);
+ return AVERROR(ret);
+ }
+ return 0;
+}
+
static int get_input_packet_mt(InputFile *f, AVPacket *pkt)
{
return av_thread_message_queue_recv(f->in_thread_queue, pkt,
@@ -4182,6 +4207,7 @@ static int process_input(int file_index)
if (ret < 0 && ifile->loop) {
if ((ret = seek_to_start(ifile, is)) < 0)
return ret;
+ init_input_thread(file_index);
ret = get_input_packet(ifile, &pkt);
if (ret == AVERROR(EAGAIN)) {
ifile->eagain = 1;
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index ffe1abd..a5eb821 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -2609,7 +2609,12 @@ loop_end:
} else if (!strcmp(key, "st")) {
int st_num = strtol(p2, NULL, 0);
av_program_add_stream_index(oc, progid, st_num);
- } else {
+ } else if (!strcmp(key, "service_provider")) {
+ av_dict_set(&program->metadata, "service_provider", p2, 0);
+ } else if (!strcmp(key, "service_name")) {
+ av_dict_set(&program->metadata, "service_name", p2, 0);
+ }
+ else {
av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
exit_program(1);
}
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 3250dde..e9f0ab8 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -59,6 +59,7 @@ typedef struct MpegTSService {
int pcr_pid;
int pcr_packet_count;
int pcr_packet_period;
+ int pcr_type; /* if the service has a/v pid: AVMEDIA_TYPE_UNKNOWN/AUDIO/VIDEO...*/
AVProgram *program;
} MpegTSService;
@@ -707,7 +708,7 @@ static void mpegts_write_sdt(AVFormatContext *s)
static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
const char *provider_name,
- const char *name)
+ const char *name, int pcr_pid)
{
MpegTSService *service;
@@ -716,7 +717,7 @@ static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
return NULL;
service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
service->sid = sid;
- service->pcr_pid = 0x1fff;
+ service->pcr_pid = pcr_pid; // was 0x1fff;
service->provider_name = av_strdup(provider_name);
service->name = av_strdup(name);
if (!service->provider_name || !service->name)
@@ -763,11 +764,11 @@ static int mpegts_init(AVFormatContext *s)
MpegTSWriteStream *ts_st;
MpegTSService *service;
AVStream *st, *pcr_st = NULL;
- AVDictionaryEntry *title, *provider;
+ AVDictionaryEntry *title, *provider, *p_pid;
int i, j;
const char *service_name;
const char *provider_name;
- int *pids;
+ int *pids, pcr_pid = 0x1fff;
int ret;
if (s->max_delay < 0) /* Not set by the caller */
@@ -786,8 +787,11 @@ static int mpegts_init(AVFormatContext *s)
service_name = title ? title->value : DEFAULT_SERVICE_NAME;
provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+ p_pid = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+ if(p_pid)
+ pcr_pid = atoi(p_pid->value);
service = mpegts_add_service(ts, ts->service_id,
- provider_name, service_name);
+ provider_name, service_name, pcr_pid);
if (!service)
return AVERROR(ENOMEM);
@@ -805,8 +809,13 @@ static int mpegts_init(AVFormatContext *s)
service_name = title ? title->value : DEFAULT_SERVICE_NAME;
provider = av_dict_get(program->metadata, "service_provider", NULL, 0);
provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+ p_pid = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+ if(p_pid)
+ pcr_pid = atoi(p_pid->value);
+ else
+ pcr_pid = 0x1fff;
service = mpegts_add_service(ts, program->id,
- provider_name, service_name);
+ provider_name, service_name, pcr_pid);
if (!service)
return AVERROR(ENOMEM);
@@ -901,12 +910,7 @@ static int mpegts_init(AVFormatContext *s)
ts_st->first_pts_check = 1;
ts_st->cc = 15;
ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT;
- /* update PCR pid by using the first video stream */
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
- service->pcr_pid == 0x1fff) {
- service->pcr_pid = ts_st->pid;
- pcr_st = st;
- }
+
if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
st->codecpar->extradata_size > 0) {
AVStream *ast;
@@ -941,17 +945,76 @@ static int mpegts_init(AVFormatContext *s)
av_freep(&pids);
- /* if no video stream, use the first stream as PCR */
- if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
- pcr_st = s->streams[0];
- ts_st = pcr_st->priv_data;
- service->pcr_pid = ts_st->pid;
- } else
- ts_st = pcr_st->priv_data;
+ MpegTSService *serv;
+ int k;
+ /* find a/v pid for PCR or any pid if no a/v found */
+ for (j = 0; j < ts->nb_services; j++) {
+ serv = ts->services[j];
+ serv->pcr_type = AVMEDIA_TYPE_UNKNOWN;
+ AVProgram *prog = serv->program;
+ if(serv->pcr_pid == 0x1fff) {
+ for (k = 0; k < prog->nb_stream_indexes; k++) {
+ st = s->streams[prog->stream_index[k]];
+ if(serv->pcr_type == AVMEDIA_TYPE_UNKNOWN &&
+ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+ st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
+ serv->pcr_type = st->codecpar->codec_type;
+ else /* video stream preference */
+ if(serv->pcr_type == AVMEDIA_TYPE_AUDIO &&
+ st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ serv->pcr_type = st->codecpar->codec_type;
+ }
+ }
+ }
+
+
+ for (j = 0; j < ts->nb_services; j++) {
+ serv = ts->services[j];
+ AVProgram *prog = serv->program;
+ if(serv->pcr_pid == 0x1fff) {
+ /* find first a/v media PID to hold PCR; calculate PCR period */
+ for (k = 0; k < prog->nb_stream_indexes; k++) {
+ st = s->streams[prog->stream_index[k]];
+ if(serv->pcr_type == AVMEDIA_TYPE_UNKNOWN ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ serv->pcr_type == AVMEDIA_TYPE_VIDEO) ||
+ (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+ serv->pcr_type == AVMEDIA_TYPE_AUDIO)) {
+ serv->pcr_pid = st->id;
+ if (ts->mux_rate > 1) {
+ serv->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
+ (TS_PACKET_SIZE * 8 * 1000);
+ } else {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ int frame_size = av_get_audio_frame_duration2(st->codecpar, 0);
+ if (!frame_size) {
+ av_log(s, AV_LOG_WARNING, "pcr_packet_period: frame size not set\n");
+ serv->pcr_packet_period = st->codecpar->sample_rate / (10 * 512);
+ } else
+ serv->pcr_packet_period = st->codecpar->sample_rate / (10 * frame_size);
+ } else {
+ // max delta PCR 0.1s
+ // TODO: should be avg_frame_rate
+ ts_st = st->priv_data;
+ serv->pcr_packet_period =
+ ts_st->user_tb.den / (10 * ts_st->user_tb.num);
+ }
+ }
+ break;
+ }
+ }
+ }
+ if (!serv->pcr_packet_period)
+ serv->pcr_packet_period = 1;
+ av_log(s, AV_LOG_VERBOSE,
+ "service 0x%x: pcr every %d pkts\n", serv->sid, serv->pcr_packet_period);
+ /* send PCR as soon as possible */
+ serv->pcr_packet_count = serv->pcr_packet_period;
+ }
+
+
if (ts->mux_rate > 1) {
- service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
- (TS_PACKET_SIZE * 8 * 1000);
ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
(TS_PACKET_SIZE * 8 * 1000);
ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME /
@@ -963,24 +1026,6 @@ static int mpegts_init(AVFormatContext *s)
/* Arbitrary values, PAT/PMT will also be written on video key frames */
ts->sdt_packet_period = 200;
ts->pat_packet_period = 40;
- if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
- if (!frame_size) {
- av_log(s, AV_LOG_WARNING, "frame size not set\n");
- service->pcr_packet_period =
- pcr_st->codecpar->sample_rate / (10 * 512);
- } else {
- service->pcr_packet_period =
- pcr_st->codecpar->sample_rate / (10 * frame_size);
- }
- } else {
- // max delta PCR 0.1s
- // TODO: should be avg_frame_rate
- service->pcr_packet_period =
- ts_st->user_tb.den / (10 * ts_st->user_tb.num);
- }
- if (!service->pcr_packet_period)
- service->pcr_packet_period = 1;
}
ts->last_pat_ts = AV_NOPTS_VALUE;
@@ -994,7 +1039,7 @@ static int mpegts_init(AVFormatContext *s)
}
// output a PCR as soon as possible
- service->pcr_packet_count = service->pcr_packet_period;
+// service->pcr_packet_count = service->pcr_packet_period;
ts->pat_packet_count = ts->pat_packet_period - 1;
ts->sdt_packet_count = ts->sdt_packet_period - 1;
@@ -1003,9 +1048,9 @@ static int mpegts_init(AVFormatContext *s)
else
av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
av_log(s, AV_LOG_VERBOSE,
- "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n",
- service->pcr_packet_period,
- ts->sdt_packet_period, ts->pat_packet_period);
+ "sdt every %d, pat/pmt every %d pkts\n",
+ service->pcr_packet_period,
+ ts->sdt_packet_period, ts->pat_packet_period);
if (ts->m2ts_mode == -1) {
if (av_match_ext(s->filename, "m2ts")) {
--
2.9.0
More information about the ffmpeg-devel
mailing list