[FFmpeg-cvslog] Merge commit '3d23a5f96ad72961c14ba3a0c2add8f2ab374b61'
Rodger Combs
git at videolan.org
Tue Sep 26 20:32:37 EEST 2017
ffmpeg | branch: master | Rodger Combs <rodger.combs at gmail.com> | Tue Sep 26 14:10:30 2017 -0300| [777d53c793a2f19b9f87d935fcb16f07ceae0dca] | committer: James Almer
Merge commit '3d23a5f96ad72961c14ba3a0c2add8f2ab374b61'
* commit '3d23a5f96ad72961c14ba3a0c2add8f2ab374b61':
dashenc: add support for assigning streams to AdaptationSets
Merged-by: Rodger Combs <rodger.combs at gmail.com>
> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=777d53c793a2f19b9f87d935fcb16f07ceae0dca
---
libavformat/dashenc.c | 223 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 180 insertions(+), 43 deletions(-)
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index 089a3e7b01..3719a1ea01 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -25,6 +25,7 @@
#endif
#include "libavutil/avassert.h"
+#include "libavutil/avutil.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
@@ -50,9 +51,14 @@ typedef struct Segment {
int n;
} Segment;
+typedef struct AdaptationSet {
+ char id[10];
+ enum AVMediaType media_type;
+} AdaptationSet;
+
typedef struct OutputStream {
AVFormatContext *ctx;
- int ctx_inited;
+ int ctx_inited, as_idx;
uint8_t iobuf[32768];
AVIOContext *out;
int packets_written;
@@ -71,6 +77,9 @@ typedef struct OutputStream {
typedef struct DASHContext {
const AVClass *class; /* Class for private options. */
+ char *adaptation_sets;
+ AdaptationSet *as;
+ int nb_as;
int window_size;
int extra_window_size;
int min_seg_duration;
@@ -79,7 +88,7 @@ typedef struct DASHContext {
int use_timeline;
int single_file;
OutputStream *streams;
- int has_video, has_audio;
+ int has_video;
int64_t last_duration;
int64_t total_duration;
char availability_start_time[100];
@@ -170,6 +179,12 @@ static void dash_free(AVFormatContext *s)
{
DASHContext *c = s->priv_data;
int i, j;
+
+ if (c->as) {
+ av_freep(&c->as);
+ c->nb_as = 0;
+ }
+
if (!c->streams)
return;
for (i = 0; i < s->nb_streams; i++) {
@@ -317,12 +332,167 @@ static void format_date_now(char *buf, int size)
}
}
+static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_index)
+{
+ DASHContext *c = s->priv_data;
+ AdaptationSet *as = &c->as[as_index];
+ int i;
+
+ avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"",
+ as->id, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
+ if (as->media_type == AVMEDIA_TYPE_VIDEO && c->max_frame_rate.num && !c->ambiguous_frame_rate)
+ avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(c->min_frame_rate, c->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", c->max_frame_rate.num, c->max_frame_rate.den);
+ avio_printf(out, ">\n");
+
+ for (i = 0; i < s->nb_streams; i++) {
+ OutputStream *os = &c->streams[i];
+
+ if (os->as_idx - 1 != as_index)
+ continue;
+
+ if (as->media_type == AVMEDIA_TYPE_VIDEO) {
+ AVStream *st = s->streams[i];
+ avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
+ i, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
+ if (st->avg_frame_rate.num)
+ avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
+ avio_printf(out, ">\n");
+ } else {
+ avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
+ i, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate);
+ avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
+ s->streams[i]->codecpar->channels);
+ }
+ output_segment_list(os, out, c);
+ avio_printf(out, "\t\t\t</Representation>\n");
+ }
+ avio_printf(out, "\t\t</AdaptationSet>\n");
+
+ return 0;
+}
+
+static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMediaType type)
+{
+ DASHContext *c = s->priv_data;
+
+ void *mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1));
+ if (!mem)
+ return AVERROR(ENOMEM);
+ c->as = mem;
+ ++c->nb_as;
+
+ *as = &c->as[c->nb_as - 1];
+ memset(*as, 0, sizeof(**as));
+ (*as)->media_type = type;
+
+ return 0;
+}
+
+static int parse_adaptation_sets(AVFormatContext *s)
+{
+ DASHContext *c = s->priv_data;
+ const char *p = c->adaptation_sets;
+ enum { new_set, parse_id, parsing_streams } state;
+ AdaptationSet *as;
+ int i, n, ret;
+ enum AVMediaType types[] = { AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_UNKNOWN };
+
+ // default: one AdaptationSet for each media type
+ if (!p) {
+ for (n = 0; types[n] != AVMEDIA_TYPE_UNKNOWN; n++) {
+ int as_idx = 0;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->codecpar->codec_type != types[n])
+ continue;
+
+ if (!as_idx) {
+ if ((ret = add_adaptation_set(s, &as, types[n])) < 0)
+ return ret;
+ as_idx = c->nb_as;
+
+ snprintf(as->id, sizeof(as->id), "%d", i);
+ }
+ c->streams[i].as_idx = as_idx;
+ }
+ }
+ goto end;
+ }
+
+ // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
+ state = new_set;
+ while (*p) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ } else if (state == new_set && av_strstart(p, "id=", &p)) {
+
+ if ((ret = add_adaptation_set(s, &as, AVMEDIA_TYPE_UNKNOWN)) < 0)
+ return ret;
+
+ n = strcspn(p, ",");
+ snprintf(as->id, sizeof(as->id), "%.*s", n, p);
+
+ p += n;
+ if (*p)
+ p++;
+ state = parse_id;
+ } else if (state == parse_id && av_strstart(p, "streams=", &p)) {
+ state = parsing_streams;
+ } else if (state == parsing_streams) {
+ AdaptationSet *as = &c->as[c->nb_as - 1];
+ OutputStream *os;
+ char idx_str[8], *end_str;
+
+ n = strcspn(p, " ,");
+ snprintf(idx_str, sizeof(idx_str), "%.*s", n, p);
+ p += n;
+
+ i = strtol(idx_str, &end_str, 10);
+ if (idx_str == end_str || i < 0 || i >= s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "Selected stream \"%s\" not found!\n", idx_str);
+ return AVERROR(EINVAL);
+ }
+
+ os = &c->streams[i];
+ if (as->media_type == AVMEDIA_TYPE_UNKNOWN) {
+ as->media_type = s->streams[i]->codecpar->codec_type;
+ } else if (as->media_type != s->streams[i]->codecpar->codec_type) {
+ av_log(s, AV_LOG_ERROR, "Mixing codec types within an AdaptationSet is not allowed\n");
+ return AVERROR(EINVAL);
+ } else if (os->as_idx) {
+ av_log(s, AV_LOG_ERROR, "Assigning a stream to more than one AdaptationSet is not allowed\n");
+ return AVERROR(EINVAL);
+ }
+ os->as_idx = c->nb_as;
+
+ if (*p == ' ')
+ state = new_set;
+ if (*p)
+ p++;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ }
+
+end:
+ // check for unassigned streams
+ for (i = 0; i < s->nb_streams; i++) {
+ OutputStream *os = &c->streams[i];
+ if (!os->as_idx) {
+ av_log(s, AV_LOG_ERROR, "Stream %d is not mapped to an AdaptationSet\n", i);
+ return AVERROR(EINVAL);
+ }
+ }
+ return 0;
+}
+
static int write_manifest(AVFormatContext *s, int final)
{
DASHContext *c = s->priv_data;
AVIOContext *out;
char temp_filename[1024];
- int ret, i, as_id = 0;
+ int ret, i;
const char *proto = avio_find_protocol_name(s->filename);
int use_rename = proto && !strcmp(proto, "file");
static unsigned int warned_non_file = 0;
@@ -393,44 +563,9 @@ static int write_manifest(AVFormatContext *s, int final)
avio_printf(out, "\t<Period id=\"0\" start=\"PT0.0S\">\n");
}
- if (c->has_video) {
- avio_printf(out, "\t\t<AdaptationSet id=\"%d\" contentType=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"", as_id++);
- if (c->max_frame_rate.num && !c->ambiguous_frame_rate)
- avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(c->min_frame_rate, c->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", c->max_frame_rate.num, c->max_frame_rate.den);
- avio_printf(out, ">\n");
-
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
- OutputStream *os = &c->streams[i];
-
- if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
- continue;
-
- avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"", i, os->codec_str, os->bandwidth_str, st->codecpar->width, st->codecpar->height);
- if (st->avg_frame_rate.num)
- avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
- avio_printf(out, ">\n");
-
- output_segment_list(&c->streams[i], out, c);
- avio_printf(out, "\t\t\t</Representation>\n");
- }
- avio_printf(out, "\t\t</AdaptationSet>\n");
- }
- if (c->has_audio) {
- avio_printf(out, "\t\t<AdaptationSet id=\"%d\" contentType=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n", as_id++);
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
- OutputStream *os = &c->streams[i];
-
- if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
- continue;
-
- avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n", i, os->codec_str, os->bandwidth_str, st->codecpar->sample_rate);
- avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n", st->codecpar->channels);
- output_segment_list(&c->streams[i], out, c);
- avio_printf(out, "\t\t\t</Representation>\n");
- }
- avio_printf(out, "\t\t</AdaptationSet>\n");
+ for (i = 0; i < c->nb_as; i++) {
+ if ((ret = write_adaptation_set(s, out, i)) < 0)
+ return ret;
}
avio_printf(out, "\t</Period>\n");
avio_printf(out, "</MPD>\n");
@@ -479,6 +614,9 @@ static int dash_init(AVFormatContext *s)
if (!c->streams)
return AVERROR(ENOMEM);
+ if ((ret = parse_adaptation_sets(s)) < 0)
+ return ret;
+
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
AVFormatContext *ctx;
@@ -559,8 +697,6 @@ static int dash_init(AVFormatContext *s)
c->ambiguous_frame_rate = 1;
}
c->has_video = 1;
- } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- c->has_audio = 1;
}
set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str));
@@ -926,6 +1062,7 @@ static int dash_check_bitstream(struct AVFormatContext *s, const AVPacket *avpkt
#define OFFSET(x) offsetof(DASHContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
+ { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
{ "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
{ "extra_window_size", "number of segments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
{ "min_seg_duration", "minimum segment duration (in microseconds)", OFFSET(min_seg_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E },
======================================================================
diff --cc libavformat/dashenc.c
index 089a3e7b01,e69e63e9ab..3719a1ea01
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@@ -24,7 -24,7 +24,8 @@@
#include <unistd.h>
#endif
+#include "libavutil/avassert.h"
+ #include "libavutil/avutil.h"
#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
@@@ -317,21 -450,163 +332,176 @@@ static void format_date_now(char *buf,
}
}
+ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_index)
+ {
+ DASHContext *c = s->priv_data;
+ AdaptationSet *as = &c->as[as_index];
+ int i;
+
- avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">\n",
++ avio_printf(out, "\t\t<AdaptationSet id=\"%s\" contentType=\"%s\" segmentAlignment=\"true\" bitstreamSwitching=\"true\"",
+ as->id, as->media_type == AVMEDIA_TYPE_VIDEO ? "video" : "audio");
++ if (as->media_type == AVMEDIA_TYPE_VIDEO && c->max_frame_rate.num && !c->ambiguous_frame_rate)
++ avio_printf(out, " %s=\"%d/%d\"", (av_cmp_q(c->min_frame_rate, c->max_frame_rate) < 0) ? "maxFrameRate" : "frameRate", c->max_frame_rate.num, c->max_frame_rate.den);
++ avio_printf(out, ">\n");
+
+ for (i = 0; i < s->nb_streams; i++) {
+ OutputStream *os = &c->streams[i];
+
+ if (os->as_idx - 1 != as_index)
+ continue;
+
+ if (as->media_type == AVMEDIA_TYPE_VIDEO) {
- avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s width=\"%d\" height=\"%d\">\n",
++ AVStream *st = s->streams[i];
++ avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"video/mp4\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
+ i, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
++ if (st->avg_frame_rate.num)
++ avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
++ avio_printf(out, ">\n");
+ } else {
+ avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/mp4\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
+ i, os->codec_str, os->bandwidth_str, s->streams[i]->codecpar->sample_rate);
+ avio_printf(out, "\t\t\t\t<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"%d\" />\n",
+ s->streams[i]->codecpar->channels);
+ }
+ output_segment_list(os, out, c);
+ avio_printf(out, "\t\t\t</Representation>\n");
+ }
+ avio_printf(out, "\t\t</AdaptationSet>\n");
+
+ return 0;
+ }
+
+ static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMediaType type)
+ {
+ DASHContext *c = s->priv_data;
+
+ void *mem = av_realloc(c->as, sizeof(*c->as) * (c->nb_as + 1));
+ if (!mem)
+ return AVERROR(ENOMEM);
+ c->as = mem;
+ ++c->nb_as;
+
+ *as = &c->as[c->nb_as - 1];
+ memset(*as, 0, sizeof(**as));
+ (*as)->media_type = type;
+
+ return 0;
+ }
+
+ static int parse_adaptation_sets(AVFormatContext *s)
+ {
+ DASHContext *c = s->priv_data;
+ const char *p = c->adaptation_sets;
+ enum { new_set, parse_id, parsing_streams } state;
+ AdaptationSet *as;
+ int i, n, ret;
+ enum AVMediaType types[] = { AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_UNKNOWN };
+
+ // default: one AdaptationSet for each media type
+ if (!p) {
+ for (n = 0; types[n] != AVMEDIA_TYPE_UNKNOWN; n++) {
+ int as_idx = 0;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ if (s->streams[i]->codecpar->codec_type != types[n])
+ continue;
+
+ if (!as_idx) {
+ if ((ret = add_adaptation_set(s, &as, types[n])) < 0)
+ return ret;
+ as_idx = c->nb_as;
+
+ snprintf(as->id, sizeof(as->id), "%d", i);
+ }
+ c->streams[i].as_idx = as_idx;
+ }
+ }
+ goto end;
+ }
+
+ // syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
+ state = new_set;
+ while (*p) {
+ if (*p == ' ') {
+ p++;
+ continue;
+ } else if (state == new_set && av_strstart(p, "id=", &p)) {
+
+ if ((ret = add_adaptation_set(s, &as, AVMEDIA_TYPE_UNKNOWN)) < 0)
+ return ret;
+
+ n = strcspn(p, ",");
+ snprintf(as->id, sizeof(as->id), "%.*s", n, p);
+
+ p += n;
+ if (*p)
+ p++;
+ state = parse_id;
+ } else if (state == parse_id && av_strstart(p, "streams=", &p)) {
+ state = parsing_streams;
+ } else if (state == parsing_streams) {
+ AdaptationSet *as = &c->as[c->nb_as - 1];
+ OutputStream *os;
+ char idx_str[8], *end_str;
+
+ n = strcspn(p, " ,");
+ snprintf(idx_str, sizeof(idx_str), "%.*s", n, p);
+ p += n;
+
+ i = strtol(idx_str, &end_str, 10);
+ if (idx_str == end_str || i < 0 || i >= s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "Selected stream \"%s\" not found!\n", idx_str);
+ return AVERROR(EINVAL);
+ }
+
+ os = &c->streams[i];
+ if (as->media_type == AVMEDIA_TYPE_UNKNOWN) {
+ as->media_type = s->streams[i]->codecpar->codec_type;
+ } else if (as->media_type != s->streams[i]->codecpar->codec_type) {
+ av_log(s, AV_LOG_ERROR, "Mixing codec types within an AdaptationSet is not allowed\n");
+ return AVERROR(EINVAL);
+ } else if (os->as_idx) {
+ av_log(s, AV_LOG_ERROR, "Assigning a stream to more than one AdaptationSet is not allowed\n");
+ return AVERROR(EINVAL);
+ }
+ os->as_idx = c->nb_as;
+
+ if (*p == ' ')
+ state = new_set;
+ if (*p)
+ p++;
+ } else {
+ return AVERROR(EINVAL);
+ }
+ }
+
+ end:
+ // check for unassigned streams
+ for (i = 0; i < s->nb_streams; i++) {
+ OutputStream *os = &c->streams[i];
+ if (!os->as_idx) {
+ av_log(s, AV_LOG_ERROR, "Stream %d is not mapped to an AdaptationSet\n", i);
+ return AVERROR(EINVAL);
+ }
+ }
+ return 0;
+ }
+
static int write_manifest(AVFormatContext *s, int final)
{
DASHContext *c = s->priv_data;
AVIOContext *out;
char temp_filename[1024];
- int ret, i, as_id = 0;
+ int ret, i;
+ const char *proto = avio_find_protocol_name(s->filename);
+ int use_rename = proto && !strcmp(proto, "file");
+ static unsigned int warned_non_file = 0;
AVDictionaryEntry *title = av_dict_get(s->metadata, "title", NULL, 0);
- snprintf(temp_filename, sizeof(temp_filename), "%s.tmp", s->filename);
+ if (!use_rename && !warned_non_file++)
+ av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n");
+
+ snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", s->filename);
ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
@@@ -472,13 -707,20 +607,16 @@@ static int dash_init(AVFormatContext *s
*ptr = '\0';
oformat = av_guess_format("mp4", NULL, NULL);
- if (!oformat) {
- ret = AVERROR_MUXER_NOT_FOUND;
- goto fail;
- }
+ if (!oformat)
+ return AVERROR_MUXER_NOT_FOUND;
c->streams = av_mallocz(sizeof(*c->streams) * s->nb_streams);
- if (!c->streams) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
+ if (!c->streams)
+ return AVERROR(ENOMEM);
+ if ((ret = parse_adaptation_sets(s)) < 0)
- goto fail;
++ return ret;
+
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
AVFormatContext *ctx;
@@@ -548,20 -798,8 +686,18 @@@
// already before being handed to this muxer, so we don't have mismatches
// between the MPD and the actual segments.
s->avoid_negative_ts = ctx->avoid_negative_ts;
- if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ AVRational avg_frame_rate = s->streams[i]->avg_frame_rate;
+ if (avg_frame_rate.num > 0) {
+ if (av_cmp_q(avg_frame_rate, c->min_frame_rate) < 0)
+ c->min_frame_rate = avg_frame_rate;
+ if (av_cmp_q(c->max_frame_rate, avg_frame_rate) < 0)
+ c->max_frame_rate = avg_frame_rate;
+ } else {
+ c->ambiguous_frame_rate = 1;
+ }
c->has_video = 1;
- } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
- c->has_audio = 1;
+ }
set_codec_str(s, st->codecpar, os->codec_str, sizeof(os->codec_str));
os->first_pts = AV_NOPTS_VALUE;
More information about the ffmpeg-cvslog
mailing list