[FFmpeg-devel] [PATCH] lavd/lavfi: allow to extract subcc.

Nicolas George george at nsup.org
Thu Dec 4 13:37:08 CET 2014


Signed-off-by: Nicolas George <george at nsup.org>
---
 doc/indevs.texi     | 14 ++++++++
 libavdevice/lavfi.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 99 insertions(+), 7 deletions(-)


Correctly rebased today, with some extra doc.

If the decoder is applied before this patch, then we can change the example
to output SRT or ASS, this would be even better.


diff --git a/doc/indevs.texi b/doc/indevs.texi
index ad823ab..abc4cc3 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -495,6 +495,14 @@ generated by the device.
 The first unlabelled output is automatically assigned to the "out0"
 label, but all the others need to be specified explicitly.
 
+The suffix "+subcc" can be appended to the output label to create an extra
+stream with the closed captions packets attached to that output
+(experimental; only for EIA-608 / CEA-708 for now).
+The subcc streams are created after all the normal streams, in the order of
+the corresponding stream.
+For example, if there is "out19+subcc", "out7+subcc" and up to "out42", the
+stream #43 is subcc for stream #7 and stream #44 is subcc for stream #19.
+
 If not specified defaults to the filename specified for the input
 device.
 
@@ -541,6 +549,12 @@ Read an audio stream and a video stream and play it back with
 ffplay -f lavfi "movie=test.avi[out0];amovie=test.wav[out1]"
 @end example
 
+ at item
+Dump decoded frames to images and closed captions to a file (experimental):
+ at example
+ffmpeg -f lavfi -i "movie=test.ts[out0+subcc]" -map v frame%08d.png -map s -c copy -f rawvideo subcc.bin
+ at end example
+
 @end itemize
 
 @section libcdio
diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c
index 1398ece..d0e6bf5 100644
--- a/libavdevice/lavfi.c
+++ b/libavdevice/lavfi.c
@@ -51,7 +51,10 @@ typedef struct {
     int *sink_stream_map;
     int *sink_eof;
     int *stream_sink_map;
+    int *sink_stream_subcc_map;
     AVFrame *decoded_frame;
+    int nb_sinks;
+    AVPacket subcc_packet;
 } LavfiContext;
 
 static int *create_all_formats(int n)
@@ -82,6 +85,7 @@ av_cold static int lavfi_read_close(AVFormatContext *avctx)
     av_freep(&lavfi->sink_stream_map);
     av_freep(&lavfi->sink_eof);
     av_freep(&lavfi->stream_sink_map);
+    av_freep(&lavfi->sink_stream_subcc_map);
     av_freep(&lavfi->sinks);
     avfilter_graph_free(&lavfi->graph);
     av_frame_free(&lavfi->decoded_frame);
@@ -89,6 +93,27 @@ av_cold static int lavfi_read_close(AVFormatContext *avctx)
     return 0;
 }
 
+static int create_subcc_streams(AVFormatContext *avctx)
+{
+    LavfiContext *lavfi = avctx->priv_data;
+    AVStream *st;
+    int stream_idx, sink_idx;
+
+    for (stream_idx = 0; stream_idx < lavfi->nb_sinks; stream_idx++) {
+        sink_idx = lavfi->stream_sink_map[stream_idx];
+        if (lavfi->sink_stream_subcc_map[sink_idx]) {
+            lavfi->sink_stream_subcc_map[sink_idx] = avctx->nb_streams;
+            if (!(st = avformat_new_stream(avctx, NULL)))
+                return AVERROR(ENOMEM);
+            st->codec->codec_id = AV_CODEC_ID_EIA_608;
+            st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+        } else {
+            lavfi->sink_stream_subcc_map[sink_idx] = -1;
+        }
+    }
+    return 0;
+}
+
 av_cold static int lavfi_read_header(AVFormatContext *avctx)
 {
     LavfiContext *lavfi = avctx->priv_data;
@@ -153,6 +178,7 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
 
     /* count the outputs */
     for (n = 0, inout = output_links; inout; n++, inout = inout->next);
+    lavfi->nb_sinks = n;
 
     if (!(lavfi->sink_stream_map = av_malloc(sizeof(int) * n)))
         FAIL(AVERROR(ENOMEM));
@@ -160,6 +186,8 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
         FAIL(AVERROR(ENOMEM));
     if (!(lavfi->stream_sink_map = av_malloc(sizeof(int) * n)))
         FAIL(AVERROR(ENOMEM));
+    if (!(lavfi->sink_stream_subcc_map = av_malloc(sizeof(int) * n)))
+        FAIL(AVERROR(ENOMEM));
 
     for (i = 0; i < n; i++)
         lavfi->stream_sink_map[i] = -1;
@@ -167,14 +195,22 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
     /* parse the output link names - they need to be of the form out0, out1, ...
      * create a mapping between them and the streams */
     for (i = 0, inout = output_links; inout; i++, inout = inout->next) {
-        int stream_idx;
-        if (!strcmp(inout->name, "out"))
-            stream_idx = 0;
-        else if (sscanf(inout->name, "out%d\n", &stream_idx) != 1) {
+        int stream_idx = 0, suffix = 0, use_subcc = 0;
+        sscanf(inout->name, "out%n%d%n", &suffix, &stream_idx, &suffix);
+        if (!suffix) {
             av_log(avctx,  AV_LOG_ERROR,
                    "Invalid outpad name '%s'\n", inout->name);
             FAIL(AVERROR(EINVAL));
         }
+        if (inout->name[suffix]) {
+            if (!strcmp(inout->name + suffix, "+subcc")) {
+                use_subcc = 1;
+            } else {
+                av_log(avctx,  AV_LOG_ERROR,
+                       "Invalid outpad suffix '%s'\n", inout->name);
+                FAIL(AVERROR(EINVAL));
+            }
+        }
 
         if ((unsigned)stream_idx >= n) {
             av_log(avctx, AV_LOG_ERROR,
@@ -192,6 +228,7 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
         }
         lavfi->sink_stream_map[i] = stream_idx;
         lavfi->stream_sink_map[stream_idx] = i;
+        lavfi->sink_stream_subcc_map[i] = !!use_subcc;
     }
 
     /* for each open output create a corresponding stream */
@@ -203,7 +240,7 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
     }
 
     /* create a sink for each output and connect them to the graph */
-    lavfi->sinks = av_malloc_array(avctx->nb_streams, sizeof(AVFilterContext *));
+    lavfi->sinks = av_malloc_array(lavfi->nb_sinks, sizeof(AVFilterContext *));
     if (!lavfi->sinks)
         FAIL(AVERROR(ENOMEM));
 
@@ -267,7 +304,7 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
     }
 
     /* fill each stream with the information in the corresponding sink */
-    for (i = 0; i < avctx->nb_streams; i++) {
+    for (i = 0; i < lavfi->nb_sinks; i++) {
         AVFilterLink *link = lavfi->sinks[lavfi->stream_sink_map[i]]->inputs[0];
         AVStream *st = avctx->streams[i];
         st->codec->codec_type = link->type;
@@ -298,6 +335,9 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
         }
     }
 
+    if ((ret = create_subcc_streams(avctx)) < 0)
+        FAIL(ret);
+
     if (!(lavfi->decoded_frame = av_frame_alloc()))
         FAIL(AVERROR(ENOMEM));
 
@@ -310,6 +350,30 @@ end:
     return ret;
 }
 
+static int create_subcc_packet(AVFormatContext *avctx, AVFrame *frame,
+                               int sink_idx)
+{
+    LavfiContext *lavfi = avctx->priv_data;
+    AVFrameSideData *sd;
+    int stream_idx, i, ret;
+
+    if ((stream_idx = lavfi->sink_stream_subcc_map[sink_idx]) < 0)
+        return 0;
+    for (i = 0; i < frame->nb_side_data; i++)
+        if (frame->side_data[i]->type == AV_FRAME_DATA_A53_CC)
+            break;
+    if (i >= frame->nb_side_data)
+        return 0;
+    sd = frame->side_data[i];
+    if ((ret = av_new_packet(&lavfi->subcc_packet, sd->size)) < 0)
+        return ret;
+    memcpy(lavfi->subcc_packet.data, sd->data, sd->size);
+    lavfi->subcc_packet.stream_index = stream_idx;
+    lavfi->subcc_packet.pts = frame->pts;
+    lavfi->subcc_packet.pos = av_frame_get_pkt_pos(frame);
+    return 0;
+}
+
 static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
 {
     LavfiContext *lavfi = avctx->priv_data;
@@ -321,9 +385,17 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
     int ret, i;
     int size = 0;
 
+    if (lavfi->subcc_packet.size) {
+        *pkt = lavfi->subcc_packet;
+        av_init_packet(&lavfi->subcc_packet);
+        lavfi->subcc_packet.size = 0;
+        lavfi->subcc_packet.data = NULL;
+        return pkt->size;
+    }
+
     /* iterate through all the graph sinks. Select the sink with the
      * minimum PTS */
-    for (i = 0; i < avctx->nb_streams; i++) {
+    for (i = 0; i < lavfi->nb_sinks; i++) {
         AVRational tb = lavfi->sinks[i]->inputs[0]->time_base;
         double d;
         int ret;
@@ -397,6 +469,12 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
         av_bprint_finalize(&meta_buf, NULL);
     }
 
+    if ((ret = create_subcc_packet(avctx, frame, min_pts_sink_idx)) < 0) {
+        av_frame_unref(frame);
+        av_packet_unref(pkt);
+        return ret;
+    }
+
     pkt->stream_index = stream_idx;
     pkt->pts = frame->pts;
     pkt->pos = av_frame_get_pkt_pos(frame);
-- 
2.1.3



More information about the ffmpeg-devel mailing list