mirror of https://git.ffmpeg.org/ffmpeg.git
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:
parent
c6bb651bce
commit
55763b6f5e
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue