FFMpeg 视频解码

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;
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值