使用FFmpeg解码之前,先定义一些接口:
根据上面的定义,可以使用C++来抽象出一个虚基类
namespace toy {
class VideoDecoder {
public:
struct Setting {
// TODO
};
virtual ~VideoDecoder() {};
// 打开解码器
virtual bool Open(const Setting& setting) = 0;
// 解码
virtual bool Decode(
const uint8_t* pkt,
const size_t pkt_size,
std::shared_ptr<uint8_t>& frame,
size_t& frame_size,
uint32_t& width,
uint32_t& height) = 0;
// 关闭解码器
virtual void Close() = 0;
};
}
头文件定义:
#pragma once
#include <stdio.h>
#include <memory>
#include <stdint.h>
#define __STDC_CONSTANT_MACROS
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#ifdef __cplusplus
};
#endif
#include "video_codec.h"
class FFmpegVideoDecoder:public toy::VideoDecoder
{
public:
FFmpegVideoDecoder();
virtual ~FFmpegVideoDecoder();
virtual bool Open(const Setting& setting);
virtual bool Decode(
const uint8_t* pkt,
const size_t pkt_size,
std::shared_ptr<uint8_t>& frame,
size_t& frame_size,
uint32_t& width,
uint32_t& height);
virtual void Close();
private:
AVCodecContext* video_codec_ctx_;
AVFrame* raw_frame_;
Setting decoder_setting_;
};
源文件实现:
#include "ffmpeg_video_decoder.h"
FFmpegVideoDecoder::FFmpegVideoDecoder()
{
av_register_all();
avcodec_register_all();
avformat_network_init();
video_codec_ctx_ = 0;
raw_frame_ = 0;
}
FFmpegVideoDecoder::~FFmpegVideoDecoder() {
Close();
}
void FFmpegVideoDecoder::Close() {
if (raw_frame_) {
av_frame_free(&raw_frame_);
raw_frame_ = NULL;
}
if (video_codec_ctx_) {
avcodec_close(video_codec_ctx_);
avcodec_free_context(&video_codec_ctx_);
video_codec_ctx_ = NULL;
}
}
bool FFmpegVideoDecoder::Open(const Setting& setting) {
decoder_setting_ = setting;
raw_frame_ = av_frame_alloc();
// 查找H264解码器
AVCodecID codec_id = AV_CODEC_ID_H264;
AVCodec* codec = avcodec_find_decoder(codec_id);
if (codec == NULL) {
fprintf(stderr, "Codec not found.\n");
Close();
return false;
}
video_codec_ctx_ = avcodec_alloc_context3(codec);
if (!video_codec_ctx_) {
printf("Could not allocate video codec context\n");
Close();
return -1;
}
if (avcodec_open2(video_codec_ctx_, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec.\n");
Close();
return false;
}
return true;
}
// 创建智能数组
#define make_array(size) std::shared_ptr<uint8_t>(new uint8_t[size], std::default_delete<uint8_t[]>())
// 解码
bool FFmpegVideoDecoder::Decode(
const uint8_t* pkt,
const size_t pkt_size,
std::shared_ptr<uint8_t>& frame,
size_t& frame_size,
uint32_t& width,
uint32_t& height) {
frame = nullptr;
frame_size = 0;
width = 0;
height = 0;
int got_picture = 0;
AVPacket video_packet;
av_init_packet(&video_packet); //AV_INPUT_BUFFER_PADDING_SIZE
video_packet.data = (uint8_t*)pkt;
video_packet.size = pkt_size;
int ret = avcodec_decode_video2(video_codec_ctx_,
raw_frame_,
&got_picture,
&video_packet);
av_packet_unref(&video_packet);
if (ret <= 0) {
char buf[1024] = { 0 };
av_make_error_string(buf, 1024, ret);
av_frame_unref(raw_frame_);
fprintf(stderr, "Decode Error.%s\n", buf);
return false;
}
if (got_picture) {
int y_size = raw_frame_->width * raw_frame_->height;
frame = make_array(y_size * 3 / 2);
memmove(frame.get(), raw_frame_->data[0], y_size);
memmove(frame.get() + y_size, raw_frame_->data[1], y_size / 4);
memmove(frame.get() + y_size * 5 / 4, raw_frame_->data[2], y_size / 4);
frame_size = y_size * 3 / 2;
width = raw_frame_->width;
height = raw_frame_->height;
}
av_frame_unref(raw_frame_);
return true;
}