1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-23 12:43:46 +02:00

lavf/tee: add special select option

This commit is contained in:
Stefano Sabatini 2013-08-08 12:11:59 +02:00
parent e6876c7b7b
commit 5ae3563359
3 changed files with 64 additions and 14 deletions

View File

@ -871,6 +871,11 @@ separated by @code{/}. If the stream specifier is not specified, the
bistream filters will be applied to all streams in the output. bistream filters will be applied to all streams in the output.
Several bitstream filters can be specified, separated by ",". Several bitstream filters can be specified, separated by ",".
@item select
Select the streams that should be mapped to the slave output,
specified by a stream specifier. If not specified, this defaults to
all the input streams.
@end table @end table
Example: encode something and both archive it in a WebM file and stream it Example: encode something and both archive it in a WebM file and stream it

View File

@ -30,6 +30,10 @@
typedef struct { typedef struct {
AVFormatContext *avf; AVFormatContext *avf;
AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
/** map from input to output streams indexes,
* disabled output streams are set to -1 */
int *stream_map;
} TeeSlave; } TeeSlave;
typedef struct TeeContext { typedef struct TeeContext {
@ -134,24 +138,54 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
AVDictionary *options = NULL; AVDictionary *options = NULL;
AVDictionaryEntry *entry; AVDictionaryEntry *entry;
char *filename; char *filename;
char *format = NULL; char *format = NULL, *select = NULL;
AVFormatContext *avf2 = NULL; AVFormatContext *avf2 = NULL;
AVStream *st, *st2; AVStream *st, *st2;
int stream_count;
if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0) if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
return ret; return ret;
if ((entry = av_dict_get(options, "f", NULL, 0))) {
format = entry->value; #define STEAL_OPTION(option, field) do { \
entry->value = NULL; /* prevent it from being freed */ if ((entry = av_dict_get(options, option, NULL, 0))) { \
av_dict_set(&options, "f", NULL, 0); field = entry->value; \
} entry->value = NULL; /* prevent it from being freed */ \
av_dict_set(&options, option, NULL, 0); \
} \
} while (0)
STEAL_OPTION("f", format);
STEAL_OPTION("select", select);
ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
if (ret < 0) if (ret < 0)
goto end; goto end;
tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
if (!tee_slave->stream_map) {
ret = AVERROR(ENOMEM);
goto end;
}
stream_count = 0;
for (i = 0; i < avf->nb_streams; i++) { for (i = 0; i < avf->nb_streams; i++) {
st = avf->streams[i]; st = avf->streams[i];
if (select) {
ret = avformat_match_stream_specifier(avf, avf->streams[i], select);
if (ret < 0) {
av_log(avf, AV_LOG_ERROR,
"Invalid stream specifier '%s' for output '%s'\n",
select, slave);
goto end;
}
if (ret == 0) { /* no match */
tee_slave->stream_map[i] = -1;
continue;
}
}
tee_slave->stream_map[i] = stream_count++;
if (!(st2 = avformat_new_stream(avf2, NULL))) { if (!(st2 = avformat_new_stream(avf2, NULL))) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);
goto end; goto end;
@ -266,6 +300,7 @@ static void close_slaves(AVFormatContext *avf)
bsf = bsf_next; bsf = bsf_next;
} }
} }
av_freep(&tee->slaves[i].stream_map);
avio_close(avf2->pb); avio_close(avf2->pb);
avf2->pb = NULL; avf2->pb = NULL;
@ -329,6 +364,15 @@ static int tee_write_header(AVFormatContext *avf)
} }
tee->nb_slaves = nb_slaves; tee->nb_slaves = nb_slaves;
for (i = 0; i < avf->nb_streams; i++) {
int j, mapped = 0;
for (j = 0; j < tee->nb_slaves; j++)
mapped += tee->slaves[j].stream_map[i] >= 0;
if (!mapped)
av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
"to any slave.\n", i);
}
return 0; return 0;
fail: fail:
@ -408,29 +452,30 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
AVPacket pkt2; AVPacket pkt2;
int ret_all = 0, ret; int ret_all = 0, ret;
unsigned i, s; unsigned i, s;
int s2;
AVRational tb, tb2; AVRational tb, tb2;
for (i = 0; i < tee->nb_slaves; i++) { for (i = 0; i < tee->nb_slaves; i++) {
avf2 = tee->slaves[i].avf; avf2 = tee->slaves[i].avf;
s = pkt->stream_index; s = pkt->stream_index;
if (s >= avf2->nb_streams) { s2 = tee->slaves[i].stream_map[s];
if (!ret_all) if (s2 < 0)
ret_all = AVERROR(EINVAL);
continue; continue;
}
if ((ret = av_copy_packet(&pkt2, pkt)) < 0 || if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
(ret = av_dup_packet(&pkt2))< 0) (ret = av_dup_packet(&pkt2))< 0)
if (!ret_all) { if (!ret_all) {
ret = ret_all; ret = ret_all;
continue; continue;
} }
tb = avf ->streams[s]->time_base; tb = avf ->streams[s ]->time_base;
tb2 = avf2->streams[s]->time_base; tb2 = avf2->streams[s2]->time_base;
pkt2.pts = av_rescale_q(pkt->pts, tb, tb2); pkt2.pts = av_rescale_q(pkt->pts, tb, tb2);
pkt2.dts = av_rescale_q(pkt->dts, tb, tb2); pkt2.dts = av_rescale_q(pkt->dts, tb, tb2);
pkt2.duration = av_rescale_q(pkt->duration, tb, tb2); pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
pkt2.stream_index = s2;
filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s]); filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]);
if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
if (!ret_all) if (!ret_all)
ret_all = ret; ret_all = ret;

View File

@ -31,7 +31,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 55 #define LIBAVFORMAT_VERSION_MAJOR 55
#define LIBAVFORMAT_VERSION_MINOR 14 #define LIBAVFORMAT_VERSION_MINOR 14
#define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_MICRO 101
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \ LIBAVFORMAT_VERSION_MINOR, \