http://www.ffmpeg.org/download.htmlhtml
執行以下命令並分析metadataios
./ffmpeg -i input.flv -c copy -f flv out.flvide
發現out.flv的metadata沒有包括input.flv的bitrate信息。函數
libavformat/flvdec.cui
未能獲取到輸入音視頻的bitrate值。spa
有兩種解決辦法:code
flvdec.c回退函數flv_read_header()到ffmpeg3.0版本orm
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中的代碼進行修改htm
修改前代碼:
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。