lavd/lavfi: allow to extract subcc.

Signed-off-by: Nicolas George <george@nsup.org>
Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
Nicolas George 2014-12-04 13:37:08 +01:00 committed by Michael Niedermayer
parent c6bb651bce
commit 55763b6f5e
2 changed files with 99 additions and 7 deletions

View File

@ -495,6 +495,14 @@ generated by the device.
The first unlabelled output is automatically assigned to the "out0" The first unlabelled output is automatically assigned to the "out0"
label, but all the others need to be specified explicitly. 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 If not specified defaults to the filename specified for the input
device. 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]" ffplay -f lavfi "movie=test.avi[out0];amovie=test.wav[out1]"
@end example @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 @end itemize
@section libcdio @section libcdio

View File

@ -51,7 +51,10 @@ typedef struct {
int *sink_stream_map; int *sink_stream_map;
int *sink_eof; int *sink_eof;
int *stream_sink_map; int *stream_sink_map;
int *sink_stream_subcc_map;
AVFrame *decoded_frame; AVFrame *decoded_frame;
int nb_sinks;
AVPacket subcc_packet;
} LavfiContext; } LavfiContext;
static int *create_all_formats(int n) 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_stream_map);
av_freep(&lavfi->sink_eof); av_freep(&lavfi->sink_eof);
av_freep(&lavfi->stream_sink_map); av_freep(&lavfi->stream_sink_map);
av_freep(&lavfi->sink_stream_subcc_map);
av_freep(&lavfi->sinks); av_freep(&lavfi->sinks);
avfilter_graph_free(&lavfi->graph); avfilter_graph_free(&lavfi->graph);
av_frame_free(&lavfi->decoded_frame); av_frame_free(&lavfi->decoded_frame);
@ -89,6 +93,27 @@ av_cold static int lavfi_read_close(AVFormatContext *avctx)
return 0; 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) av_cold static int lavfi_read_header(AVFormatContext *avctx)
{ {
LavfiContext *lavfi = avctx->priv_data; LavfiContext *lavfi = avctx->priv_data;
@ -153,6 +178,7 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
/* count the outputs */ /* count the outputs */
for (n = 0, inout = output_links; inout; n++, inout = inout->next); 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))) if (!(lavfi->sink_stream_map = av_malloc(sizeof(int) * n)))
FAIL(AVERROR(ENOMEM)); FAIL(AVERROR(ENOMEM));
@ -160,6 +186,8 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx)
FAIL(AVERROR(ENOMEM)); FAIL(AVERROR(ENOMEM));
if (!(lavfi->stream_sink_map = av_malloc(sizeof(int) * n))) if (!(lavfi->stream_sink_map = av_malloc(sizeof(int) * n)))
FAIL(AVERROR(ENOMEM)); FAIL(AVERROR(ENOMEM));
if (!(lavfi->sink_stream_subcc_map = av_malloc(sizeof(int) * n)))
FAIL(AVERROR(ENOMEM));
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
lavfi->stream_sink_map[i] = -1; 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, ... /* parse the output link names - they need to be of the form out0, out1, ...
* create a mapping between them and the streams */ * create a mapping between them and the streams */
for (i = 0, inout = output_links; inout; i++, inout = inout->next) { for (i = 0, inout = output_links; inout; i++, inout = inout->next) {
int stream_idx; int stream_idx = 0, suffix = 0, use_subcc = 0;
if (!strcmp(inout->name, "out")) sscanf(inout->name, "out%n%d%n", &suffix, &stream_idx, &suffix);
stream_idx = 0; if (!suffix) {
else if (sscanf(inout->name, "out%d\n", &stream_idx) != 1) {
av_log(avctx, AV_LOG_ERROR, av_log(avctx, AV_LOG_ERROR,
"Invalid outpad name '%s'\n", inout->name); "Invalid outpad name '%s'\n", inout->name);
FAIL(AVERROR(EINVAL)); 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) { if ((unsigned)stream_idx >= n) {
av_log(avctx, AV_LOG_ERROR, 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->sink_stream_map[i] = stream_idx;
lavfi->stream_sink_map[stream_idx] = i; lavfi->stream_sink_map[stream_idx] = i;
lavfi->sink_stream_subcc_map[i] = !!use_subcc;
} }
/* for each open output create a corresponding stream */ /* 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 */ /* 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) if (!lavfi->sinks)
FAIL(AVERROR(ENOMEM)); 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 */ /* 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]; AVFilterLink *link = lavfi->sinks[lavfi->stream_sink_map[i]]->inputs[0];
AVStream *st = avctx->streams[i]; AVStream *st = avctx->streams[i];
st->codec->codec_type = link->type; 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())) if (!(lavfi->decoded_frame = av_frame_alloc()))
FAIL(AVERROR(ENOMEM)); FAIL(AVERROR(ENOMEM));
@ -310,6 +350,30 @@ end:
return ret; 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) static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
{ {
LavfiContext *lavfi = avctx->priv_data; LavfiContext *lavfi = avctx->priv_data;
@ -321,9 +385,17 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
int ret, i; int ret, i;
int size = 0; 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 /* iterate through all the graph sinks. Select the sink with the
* minimum PTS */ * 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; AVRational tb = lavfi->sinks[i]->inputs[0]->time_base;
double d; double d;
int ret; int ret;
@ -397,6 +469,12 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt)
av_bprint_finalize(&meta_buf, NULL); 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->stream_index = stream_idx;
pkt->pts = frame->pts; pkt->pts = frame->pts;
pkt->pos = av_frame_get_pkt_pos(frame); pkt->pos = av_frame_get_pkt_pos(frame);