FFmpeg官网:
http://www.ffmpeg.org/download.html
现象:
执行如下命令并分析metadata
./ffmpeg -i input.flv -c copy -f flv out.flv
发现out.flv的metadata没有包括input.flv的bitrate信息。
原因:
libavformat/flvdec.c
未能获取到输入音视频的bitrate值。
有两种解决办法:
解决方法一:
flvdec.c回退函数flv_read_header()到ffmpeg3.0版本
static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return NULL;
st->codec->codec_type = codec_type;
if (s->nb_streams>=3 ||( s->nb_streams==2
&& s->streams[0]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE
&& s->streams[1]->codec->codec_type != AVMEDIA_TYPE_SUBTITLE))
s->ctx_flags &= ~AVFMTCTX_NOHEADER;
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
return st;
}
static int flv_read_header(AVFormatContext *s)
{
int offset, flags;
FLVContext *flv = s->priv_data;
avio_skip(s->pb, 4);
flags = avio_r8(s->pb);
s->ctx_flags |= AVFMTCTX_NOHEADER;
if (flags & FLV_HEADER_FLAG_HASVIDEO)
if (!create_stream(s, AVMEDIA_TYPE_VIDEO))
return AVERROR(ENOMEM);
if (flags & FLV_HEADER_FLAG_HASAUDIO)
if (!create_stream(s, AVMEDIA_TYPE_AUDIO))
return AVERROR(ENOMEM);
// Flag doesn't indicate whether or not there is script-data present. Must
// create that stream if it's encountered.
offset = avio_rb32(s->pb);
avio_seek(s->pb, offset, SEEK_SET);
avio_skip(s->pb, 4);
s->start_time = 0;
flv->sum_flv_tag_size = 0;
return 0;
}
则通过函数open_input_file() -> avformat_open_input() -> s->iformat->read_header(s)即可以创建video和audio的stream。然后通过 avformat_find_stream_info() -> read_frame_internal(ic, &pkt1) -> ff_read_packet() -> s->iformat->read_packet(s, pkt),回调函数flv_read_packet(AVFormatContext *s, AVPacket *pkt)获取metadata中的bitrate。
解决方法二:
对于flvdec.c中的代码进行修改
修改前代码:
static int amf_parse_object(AVFormatContext *s, AVStream *astream,
AVStream *vstream, const char *key,
int64_t max_pos, int depth)
{
... ...
if (key) {
apar = astream ? astream->codecpar : NULL;
vpar = vstream ? vstream->codecpar : NULL;
// stream info doesn't live any deeper than the first object
if (depth == 1) {
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
if (!strcmp(key, "duration"))
s->duration = num_val * AV_TIME_BASE;
else if (!strcmp(key, "videodatarate") && vpar &&
0 <= (int)(num_val * 1024.0))
vpar->bit_rate = num_val * 1024.0;
else if (!strcmp(key, "audiodatarate") && apar &&
0 <= (int)(num_val * 1024.0))
apar->bit_rate = num_val * 1024.0;
else if (!strcmp(key, "datastream")) {
AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return AVERROR(ENOMEM);
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
} else if (flv->trust_metadata) {
if (!strcmp(key, "videocodecid") && vpar) {
flv_set_video_codec(s, vstream, num_val, 0);
} else if (!strcmp(key, "audiocodecid") && apar) {
int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
flv_set_audio_codec(s, astream, apar, id);
} else if (!strcmp(key, "audiosamplerate") && apar) {
apar->sample_rate = num_val;
} else if (!strcmp(key, "audiosamplesize") && apar) {
apar->bits_per_coded_sample = num_val;
} else if (!strcmp(key, "stereo") && apar) {
apar->channels = num_val + 1;
apar->channel_layout = apar->channels == 2 ?
AV_CH_LAYOUT_STEREO :
AV_CH_LAYOUT_MONO;
} else if (!strcmp(key, "width") && vpar) {
vpar->width = num_val;
} else if (!strcmp(key, "height") && vpar) {
vpar->height = num_val;
}
}
}
if (amf_type == AMF_DATA_TYPE_STRING) {
if (!strcmp(key, "encoder")) {
int version = -1;
if (1 == sscanf(str_val, "Open Broadcaster Software v0.%d", &version)) {
if (version > 0 && version <= 655)
flv->broken_sizes = 1;
}
} else if (!strcmp(key, "metadatacreator") && !strcmp(str_val, "MEGA")) {
flv->broken_sizes = 1;
}
}
... ...
}
修改后代码
typedef struct FLVContext {
const AVClass *class; ///< Class for private options.
int trust_metadata; ///< configure streams according onMetaData
int wrong_dts; ///< wrong dts due to negative cts
uint8_t *new_extradata[FLV_STREAM_TYPE_NB];
int new_extradata_size[FLV_STREAM_TYPE_NB];
int last_sample_rate;
int last_channels;
struct {
int64_t dts;
int64_t pos;
} validate_index[2];
int validate_next;
int validate_count;
int searched_for_end;
uint8_t resync_buffer[2*RESYNC_BUFFER_SIZE];
int broken_sizes;
int sum_flv_tag_size;
int last_keyframe_stream_index;
int keyframe_count;
int64_t *keyframe_times;
int64_t *keyframe_filepositions;
int64_t video_bit_rate;
int64_t audio_bit_rate;
} FLVContext;
static AVStream *create_stream(AVFormatContext *s, int codec_type)
{
FLVContext *flv = s->priv_data;
AVStream *st = avformat_new_stream(s, NULL);
if (!st)
return NULL;
st->codecpar->codec_type = codec_type;
if (codec_type == FLV_STREAM_TYPE_VIDEO)
st->codecpar->bit_rate = flv->video_bit_rate;
if (codec_type == FLV_STREAM_TYPE_AUDIO)
st->codecpar->bit_rate = flv->audio_bit_rate;
if (s->nb_streams>=3 ||( s->nb_streams==2
&& s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
&& s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE))
s->ctx_flags &= ~AVFMTCTX_NOHEADER;
avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
flv->last_keyframe_stream_index = s->nb_streams - 1;
add_keyframes_index(s);
return st;
}
static int amf_parse_object(AVFormatContext *s, AVStream *astream,
AVStream *vstream, const char *key,
int64_t max_pos, int depth)
{
... ...
if (key) {
apar = astream ? astream->codecpar : NULL;
vpar = vstream ? vstream->codecpar : NULL;
// stream info doesn't live any deeper than the first object
if (depth == 1) {
if (amf_type == AMF_DATA_TYPE_NUMBER ||
amf_type == AMF_DATA_TYPE_BOOL) {
if (!strcmp(key, "duration"))
s->duration = num_val * AV_TIME_BASE;
else if (!strcmp(key, "videodatarate") &&
0 <= (int)(num_val * 1024.0))
flv->video_bit_rate = num_val * 1024.0;
else if (!strcmp(key, "audiodatarate") &&
0 <= (int)(num_val * 1024.0))
flv->audio_bit_rate = num_val * 1024.0;
else if (!strcmp(key, "datastream")) {
AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
if (!st)
return AVERROR(ENOMEM);
st->codecpar->codec_id = AV_CODEC_ID_TEXT;
} else if (flv->trust_metadata) {
if (!strcmp(key, "videocodecid") && vpar) {
flv_set_video_codec(s, vstream, num_val, 0);
} else if (!strcmp(key, "audiocodecid") && apar) {
int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
flv_set_audio_codec(s, astream, apar, id);
} else if (!strcmp(key, "audiosamplerate") && apar) {
apar->sample_rate = num_val;
} else if (!strcmp(key, "audiosamplesize") && apar) {
apar->bits_per_coded_sample = num_val;
} else if (!strcmp(key, "stereo") && apar) {
apar->channels = num_val + 1;
apar->channel_layout = apar->channels == 2 ?
AV_CH_LAYOUT_STEREO :
AV_CH_LAYOUT_MONO;
} else if (!strcmp(key, "width") && vpar) {
vpar->width = num_val;
} else if (!strcmp(key, "height") && vpar) {
vpar->height = num_val;
}
}
}
if (amf_type == AMF_DATA_TYPE_STRING) {
if (!strcmp(key, "encoder")) {
int version = -1;
if (1 == sscanf(str_val, "Open Broadcaster Software v0.%d", &version)) {
if (version > 0 && version <= 655)
flv->broken_sizes = 1;
}
} else if (!strcmp(key, "metadatacreator") && !strcmp(str_val, "MEGA")) {
flv->broken_sizes = 1;
}
}
... ...
}
结论:
能够成功的将输入flv流中metadata传递到输出metadata。