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:
parent
e6876c7b7b
commit
5ae3563359
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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, \
|
||||||
|
Loading…
Reference in New Issue
Block a user