ffmpeg的版本众多,从2010年开始计算的项目的话,基本上还在使用的有ffmpeg2/3/4/5/6,最近几年版本彪的比较厉害,直接4/5/6,大版本之间接口有一些变化,特别是一些废弃接口被彻底删除了,而网络上的各种文章几乎都是ffmpeg3左右为主的,所以本人在写这个全功能播放组件的时候,也特别注意了兼容性的问题。编写这个全能播放组件,面对用户各种各样的需求,当然需要从ffmpeg2兼容到ffmpeg6以及后续的版本,现在用的最多的还是ffmpeg4版本,目测三五年后会陆续切换到ffmpeg5/ffmpeg6,主要是支持的格式多了,尤其是某些新标准的编解码的效率更高。在ffmpeg提供的头文件接口中,并没有提供ffmpeg的大版本号,只提供了字符串版本,所以需要通过子库的主版本号来定义一个ffmpeg的版本号,比如编解码库LIBAVCODEC_VERSION_MAJOR,56=ffmpeg2/57=ffmpeg3/58=ffmpeg4/59=ffmpeg5/60=ffmpeg6,这个编解码库就是ffmpeg的核心,看家的本领都在里面,个人觉得ffmpeg最牛逼的就是编解码和滤镜。在兼容各个版本的这条路上,大致整理了以下几条:
int FFmpegHelper::getRotate(AVStream *stream)
{
int rotate = 0;
//测试发现ffmpeg2不支持旋转滤镜
#if (FFMPEG_VERSION_MAJOR < 3)
return rotate;
#endif
#if (FFMPEG_VERSION_MAJOR < 5)
AVDictionaryEntry *tag = NULL;
tag = av_dict_get(stream->metadata, "rotate", NULL, 0);
if (tag) {
rotate = atoi(tag->value);
}
#else
//从ffplay源码中找到的方法
double theta = 0;
quint8 *displaymatrix = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (displaymatrix) {
theta = -av_display_rotation_get((qint32 *) displaymatrix);
theta -= 360 * floor(theta / 360 + 0.9 / 360);
rotate = theta;
}
#endif
return rotate;
}
void FFmpegHelper::setRotate(AVStream *stream, int rotate)
{
#if (FFMPEG_VERSION_MAJOR < 5)
av_dict_set(&stream->metadata, "rotate", QString::number(rotate).toUtf8().constData(), 0);
#else
quint8 *sidedata = av_stream_new_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, sizeof(qint32) * 9);
if (sidedata) {
av_display_rotation_set((qint32 *)sidedata, rotate);
}
#endif
}
AVCodecID FFmpegHelper::getCodecId(AVStream *stream)
{
qint64 bitrate;
int id, type, format, width, height, sampleRate, channelCount, profile;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
return AVCodecID(id);
}
QString FFmpegHelper::getCodecName(AVStream *stream)
{
AVCodecID id = getCodecId(stream);
return (id == AV_CODEC_ID_NONE ? "none" : avcodec_descriptor_get(id)->name);
}
qint64 FFmpegHelper::getBitRate(int width, int height)
{
qint64 bitRate = 400;
int size = width * height;
if (size <= (640 * 360)) {
bitRate = 400;
} else if (size <= (960 * 540)) {
bitRate = 900;
} else if (size <= (1280 * 720)) {
bitRate = 1500;
} else if (size <= (1920 * 1080)) {
bitRate = 3000;
} else if (size <= (2560 * 1440)) {
bitRate = 3500;
} else if (size <= (3840 * 2160)) {
bitRate = 6000;
}
return bitRate * 1000;
}
qint64 FFmpegHelper::getBitRate(AVStream *stream)
{
qint64 bitrate;
int id, type, format, width, height, sampleRate, channelCount, profile;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
return bitrate;
}
int FFmpegHelper::getFormat(AVStream *stream)
{
qint64 bitrate;
int id, type, format, width, height, sampleRate, channelCount, profile;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
return format;
}
AVMediaType FFmpegHelper::getMediaType(AVStream *stream)
{
qint64 bitrate;
int id, type, format, width, height, sampleRate, channelCount, profile;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
return AVMediaType(type);
}
void FFmpegHelper::getResolution(AVStream *stream, int &width, int &height)
{
qint64 bitrate;
int id, type, format, sampleRate, channelCount, profile;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
}
void FFmpegHelper::getAudioInfo(AVStream *stream, int &sampleRate, int &channelCount, int &profile)
{
qint64 bitrate;
int id, type, format, width, height;
getStreamInfo(stream, id, type, format, bitrate, width, height, sampleRate, channelCount, profile);
}
void FFmpegHelper::getStreamInfo(AVStream *stream, int &id, int &type, int &format, qint64 &bitrate, int &width, int &height, int &sampleRate, int &channelCount, int &profile)
{
#if (FFMPEG_VERSION_MAJOR < 3)
type = stream->codec->codec_type;
if (type == AVMEDIA_TYPE_VIDEO) {
format = stream->codec->pix_fmt;
} else if (type == AVMEDIA_TYPE_AUDIO) {
format = stream->codec->sample_fmt;
}
id = stream->codec->codec_id;
bitrate = stream->codec->bit_rate;
width = stream->codec->width;
height = stream->codec->height;
sampleRate = stream->codec->sample_rate;
channelCount = stream->codec->channels;
profile = stream->codec->profile;
#else
id = stream->codecpar->codec_id;
type = stream->codecpar->codec_type;
format = stream->codecpar->format;
bitrate = stream->codecpar->bit_rate;
width = stream->codecpar->width;
height = stream->codecpar->height;
sampleRate = stream->codecpar->sample_rate;
channelCount = stream->codecpar->channels;
profile = stream->codecpar->profile;
#endif
}
int FFmpegHelper::copyContext(AVStream *streamIn, AVStream *streamOut)
{
int result = -1;
//设置 codec_tag = 0 这个很关键(不加保存的数据可能不正确)
#if (FFMPEG_VERSION_MAJOR < 3)
result = avcodec_copy_context(streamOut->codec, streamIn->codec);
streamOut->codec->codec_tag = 0;
#else
result = avcodec_parameters_copy(streamOut->codecpar, streamIn->codecpar);
streamOut->codecpar->codec_tag = 0;
#endif
return result;
}
int FFmpegHelper::copyContext(AVCodecContext *avctx, AVStream *stream, bool from)
{
int result = -1;
#if (FFMPEG_VERSION_MAJOR < 3)
if (from) {
result = avcodec_copy_context(stream->codec, avctx);
} else {
result = avcodec_copy_context(avctx, stream->codec);
}
#else
if (from) {
result = avcodec_parameters_from_context(stream->codecpar, avctx);
} else {
result = avcodec_parameters_to_context(avctx, stream->codecpar);
}
#endif
return result;
}