1 头文件 和 库
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libswscale/swscale.h>
}
#include <QContiguousCache>
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib, "swscale.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
#pragma comment(lib, "swresample.lib")
//定义结构体
struct _FFmpeg {
AVFormatContext *fmtCtx;
AVCodec *codec;
AVCodecContext *codecCtx;
AVStream *stream;
int streamIndex;
_FFmpeg():fmtCtx(0),
codec(0),
codecCtx(0),
stream(0),
streamIndex(0)
{
}
} FFmpeg;
struct _Video : public _FFmpeg {
uint8_t *dest_data[4];
int dest_linesize[4];
int dest_bufsize;
enum AVPixelFormat pix_format;
int frameWidth, frameHeight;
QFile outFile;
} video;
struct _Audio :public _FFmpeg {
}audio;
//定义帧 变量
AVFrame *frame;
AVPacket packet;
QByteArray audioData, videoData;
2 初始化
//初始化
int ret = 0;
av_register_all();
avcodec_register_all();
avformat_network_init();
3 打开视频文件
// <0 : failed
// >=0 : index
int open_codec_context(AVMediaType type)
{
AVDictionary *opts = NULL;
if (!FFmpeg.fmtCtx) {
qDebug()<< "fmtCtx is null";
return -1;
}
int index = av_find_best_stream(FFmpeg.fmtCtx, type, -1, -1, NULL, 0);
if (index < 0) {
qDebug() << "find best stream failed";
outError(index);
return -1;
}
FFmpeg.stream = FFmpeg.fmtCtx->streams[index];
if (type == AVMEDIA_TYPE_VIDEO) {
qDebug() << "Video stream time base: {" <<FFmpeg.stream->time_base.num
<<", "<< FFmpeg.stream->time_base.den << "}";
}
FFmpeg.codecCtx = FFmpeg.stream->codec;
FFmpeg.codec = avcodec_find_decoder(FFmpeg.codecCtx->codec_id);
if (!FFmpeg.codec) {
qDebug() << "find codec failed";
return -1;
}
// /* Init the decoders, with or without reference counting */
//without
av_dict_set(&opts, "refcounted_frames", "0", 0);
//with
// av_dict_set(&opts, "refcounted_frames", "1", 0);
int ret = avcodec_open2(FFmpeg.codecCtx, FFmpeg.codec, &opts) ;
if (ret < 0) {
qDebug() << "open codec failed";
outError(ret);
return -2;
}
return index;
}
qDebug() << "URL -------" << url;
ret = avformat_open_input(&FFmpeg.fmtCtx, url, NULL, NULL);
if (ret < 0) {
qDebug() << "open url error";
outError(ret);
return -1;
}
ret = avformat_find_stream_info(FFmpeg.fmtCtx, NULL);
if (ret < 0) {
qDebug() << "find stream failed";
outError(ret);
return -1;
}
ret = open_codec_context(AVMEDIA_TYPE_VIDEO);
if (ret >= 0) {
video.streamIndex = ret;
video.codecCtx = FFmpeg.codecCtx;
video.stream = FFmpeg.stream;
video.codec = FFmpeg.codec;
video.frameWidth = video.codecCtx->width;
video.frameHeight = video.codecCtx->height;
video.pix_format = video.codecCtx->pix_fmt;
int size = sizeof(uint8_t);
video.dest_bufsize = av_image_alloc(video.dest_data, video.dest_linesize,
video.frameWidth, video.frameHeight, video.pix_format, size);
if (video.dest_bufsize < 0) {
qDebug() << "alloc image buf failed";
return -2;
}
qDebug() << "oepn video context success";
}
else {
qDebug() << "open video context failed";
}
4 输出视频文件 信息
av_dump_format(FFmpeg.fmtCtx, 0, url, 0);
if (!video.stream && !audio.stream) {
qDebug() << "Could not find audio or video stream.";
return -1;
}
5 申请内存
frame = av_frame_alloc();
if (frame == nullptr) {
qDebug() << "alloc frame failed";
return -1;
}
av_init_packet(&packet);
packet.data = nullptr;
packet.size = 0;
6 循环解码
int ret = 0;
int gotFrame = 0;
while(1) {
int get = av_read_frame(FFmpeg.fmtCtx, &packet);
if (get < 0) {
//循环播放(回到0帧)
av_seek_frame(FFmpeg.fmtCtx, -1, FFmpeg.stream->start_time, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(FFmpeg.codecCtx);
video_frame_count = 0;
av_init_packet(&packet);
packet.data = nullptr;
packet.size = 0;
//break;
}
do {
if (m_STOP) {
goto stop;
}
ret = decode_packet_new();
if (ret <= 0) {
break;
}
packet.data += ret;
packet.size -= ret;
} while( packet.size > 0);
// 内存释放
av_packet_unref(&packet);
}
packet.data = nullptr;
packet.size = 0;
do {
if (m_STOP) {
goto stop;
}
ret = decode_packet_new();
} while(gotFrame);
stop:
return ;
7 解码函数
int DecodeRtmp::decode_packet_new()
{
AVCodecContext* avCodecContext = video.codecCtx;
if (packet.stream_index != video.streamIndex) {
return -1;
}
//发送解码包
int ret = avcodec_send_packet(avCodecContext, &packet);
//av_packet_unref(&packet);
if (ret != 0) {
//outError(ret);
avcodec_flush_buffers(avCodecContext);
return -1;
}
//接收解码包 并循环解码 拷贝数据
ret = avcodec_receive_frame(avCodecContext, frame);
while (ret == 0)
{
if (ret != 0) {
qDebug() << ("avcodec_receive_frame failed !");
//outError(ret);
avcodec_flush_buffers(avCodecContext);
return -1;
}
video_frame_count++;
//拷贝解码数据
av_image_copy(video.dest_data, video.dest_linesize, (const uint8_t **)frame->data, \
frame->linesize, video.pix_format, frame->width, frame->height);
//sws_scale 可以进行格式转换
videoData.clear();
videoData.append((const char *)video.dest_data[0], video.dest_bufsize);
emit readyVideo(videoData, frame->width, frame->height, video.pix_format);
ret = avcodec_receive_frame(avCodecContext, frame);
}
//mSleep(DecodeTime);
return FFMIN(ret, packet.size);
}
8 内存释放
if (video.codecCtx)
avcodec_close(video.codecCtx);
//if (audio.codecCtx)
// avcodec_close(audio.codecCtx);
if (FFmpeg.fmtCtx)
avformat_close_input(&FFmpeg.fmtCtx);
if (frame)
av_frame_free(&frame);
if (video.dest_data[0])
{
av_free(video.dest_data[0]);
video.dest_data[0] = nullptr;
}