Subject: [FFmpeg-devel] [PATCH 2/2] lavd/lavfi: allow to extract subcc.
From: Nicolas George <[email-masked]>
To: [email-masked]
Date: Mon, 10 Mar 2014 11:56:47 +0100
List-Id: FFmpeg development discussions and patches <ffmpeg-devel.ffmpeg.org>

Signed-off-by: Nicolas George <[email-masked]>
---
 doc/indevs.texi     |  9 ++++++
 libavdevice/lavfi.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 84 insertions(+), 5 deletions(-)

With this patch, it becomes possible to dump the subcc data, just like
Anshul's program did, using the ffmpeg command-line tool.

With a decoder, hopefully, it would be possible to use them like any kind of
text subtitles.

It requires using "-f lavfi -i movie=file" instead of just "-i file". I am
not sure what the long term solution would be: implementing that really at
the demuxer level, including frame reordering, or acknowledging that the
protocol/demuxer/decoder stack model is too limited for some features
(including, but not limited to, this one) and move to a more general model,
possibly based on complex graphs.

diff --git a/doc/indevs.texi b/doc/indevs.texi
index 93fbbe8..cf948c6 100644
--- a/doc/indevs.texi
+++ b/doc/indevs.texi
@@ -324,6 +324,9 @@ number starting from 0 corresponding to the mapped input stream
 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).
 
 If not specified defaults to the filename specified for the input
 device.
@@ -371,6 +374,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
 
+@item
+Dump decoded frames to images and closed captions to a file (experimental):
+@example
+ffmpeg -f lavfi -i "movie=test.ts[out0+subcc]" -map v frame%08d.png -map s -c copy -f rawvideo subcc.bin
+@end example
+
 @end itemize
 
 @section libdc1394
diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c
index 1ea7ea7..83fe490 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_CEA_708;
+            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,10 +195,10 @@ 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;
+        int stream_idx, use_subcc = 0;
         if (!strcmp(inout->name, "out"))
             stream_idx = 0;
-        else if (sscanf(inout->name, "out%d\n", &stream_idx) != 1) {
+        else if (sscanf(inout->name, "out%d+subcc%n\n", &stream_idx, &use_subcc) != 1) {
             av_log(avctx,  AV_LOG_ERROR,
                    "Invalid outpad name '%s'\n", inout->name);
             FAIL(AVERROR(EINVAL));
@@ -200,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 */
@@ -211,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(sizeof(AVFilterContext *) * avctx->nb_streams);
+    lavfi->sinks = av_malloc(sizeof(AVFilterContext *) * lavfi->nb_sinks);
     if (!lavfi->sinks)
         FAIL(AVERROR(ENOMEM));
 
@@ -271,7 +300,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;
@@ -302,6 +331,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));
 
@@ -314,6 +346,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;
@@ -325,9 +381,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;
@@ -401,6 +465,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);
-- 
1.9.0

_______________________________________________
ffmpeg-devel mailing list
[email-masked]
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel