[FFmpeg-devel] [PATCH 2/2] lavd/lavfi: allow to extract subcc.
Stefano Sabatini
stefasab at gmail.com
Mon Mar 17 19:48:37 CET 2014
On date Monday 2014-03-10 11:56:47 +0100, Nicolas George encoded:
> Signed-off-by: Nicolas George <george at nsup.org>
> ---
> 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).
Please specify what kind of data is supported (i.e. the name of the
standard).
>
> 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
>
> + 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
rawvideo for subcc looks a bit like a hack, but whatever...
> + at 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) {
This is not completely robust, for example with "out0+subc" (missing
character typo).
I'd suggest something like:
else {
const char suffix[32];
int n = sscanf(inout->name, "out%d%s\n", &stream_idx, &suffix);
if (n == 2 && !strcmp(suffix, "+subcc")) {
use_subcc = 1;
} else {
av_log(ctx, AV_LOG_ERROR, "Invalid "%s" suffix found in outpad name '%s'\n", inout->name);
FAIL(AVERROR(EINVAL));
}
}
> 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;
> + }
> +
LGTM otherwise, thanks, assuming it's tested and working.
--
FFmpeg = Fundamental & Funny Monstrous Plastic Enlightened Gadget
More information about the ffmpeg-devel
mailing list