实现《魔影》音视频模块开发

1.音视频采样编码的基本原理

将模拟信号转化为数字信号,隔段时间进行采样
在这里插入图片描述

2.ffmpeg的基本情况

介绍

Fmpeg 是领先的多媒体框架,能够解码、编码、转码、混合、解密、流媒体、过滤和播放人类和机器创造的几乎所有东西。它支持最晦涩的古老格式,直到最尖端的格式。无论它们是由某个标准委员会、社区还是公司设计的。它还具有高度的便携性。
FFmpeg 可以在 多个平台构建环境、机器架构和配置下编译、运行,并通过测试基础设施 FATE。

FFmpeg命令行工具获取

  • 下载地址
    • 访问FFmpeg官网(http://ffmpeg.org)选择Download,选择Windows Package进入Zeranoe FFmpeg网站。
    • 注意不要直接从FFmpeg官网下载源代码。
  • 版本说明
    • Zeranoe网站中的FFmpeg分为3个版本:
      • static:只包含3个体积很大的exe文件。
      • shared:除了3个体积较小的exe文件外,还包含了dll动态库文件
      • Dev:只包含了开发用的头文件(*.h)和导入库文件(*.lib)
  • 一般使用命令行时下载Static或者Shared版本就可以了
  • 进行开发需要下载Dev版本

ffmpeg.exe的使用

  • 命令格式

    • 功能
      ffmpeg.exe用于视频的转码

    • 最简单的命令

      ffmpeg -i input.avi -b:v 640k output.ts
      
    • 该命令将当前文件夹下的input.avi转换为output.ts文件,并将其文件视频的码率设置为640kbps。

    • 命令格式

      ffmpeg -i (输入文件路径) -b:v (输出视频码率) (输出文件路径)
      
    • 命令参数

    参数说明
    -h帮助
    -i filename输入文件
    -t duration设置处理时间,格式为hh:mm:ss
    -ss duration设置起始时间,格式为hh:mm:ss
    -b:v bltrate设置视频码率
    -b:a bitrate设置音频码率
    -r fps设置帧率
    -s wxh设置分辨率,格式为WxH(用*也可以)
    -c:v codec设置视频编码器
    -c:a codec设置音频编码器
    -ar freq设置音频采样率
      如: ffmpeg -i video.mkv video.mp4 将video从MKV格式转码为MP4格式并另外保存
    

ffplay.exe的使用

  • 命令格式

    • 功能
      用于视频的播放

    • 最简单的命令

      ffplay input.avi
      
    • 该命令将播放当前文件夹下的input.avi文件。

    • 命令格式

      ffmpeg (输入文件路径)
      
    • 命令参数

    快捷键说明
    q,ESC退出
    p,空格暂停
    鼠标右键屏幕跳转到指定位置

解封装

  • av+register_all():注册所有组件

  • avformat_network_init()

  • avformat_open_input(…):打开输入视频文件

    • 确保av_register_all、avformat_network_init已经停用
    • AVFormatContext **ps
    • const char *url
    • AVInputFormat *fmt
    • AVDIctionary **options
  • avformat_find_stream_info(…):获取视频文件信息

  • avcodec_find_decoder():查找解码器

  • avcodec_open2():打开解码器

  • av_find_best_stream(…)

  • AVFormatContext (封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息)

    • AVIOContext *pd;char filename[1024];
    • iformat //输入视频的AVInputFormat
    • unsigned int nb_streams; //输入视频的AVStream
    • AVStream **streams; //输入视频的AVStream []数组
    • int64_t duration; //AV_TIME_BASE 输入视频的时长(以微秒为单位)
    • int64_t bit_rate; //输入视频码率
    • void avformat_close_input(AVFormatContent **s); //关闭输入视频文件,释放空间
  • AVInputFormat (每种封装格式(例如FLV,MKV,MP4,AVI)对应一个结构体)

    • name:封装格式名称
    • long_name:封装格式的长名称
    • extensions:封装格式的扩展名
    • id:封装格式ID
    • 一些封装格式处理的接口函数
  • AVStream (视频文件中每个视频(音频)流对应一个结构体)

    • AVCodecContext *codec; //过时了
    • id //序号
    • AVRational time_base; //分数表示,减少精度损失
    • int64_t duration; //毫秒 duration (time_base,num/(double)time_base.den) 1000毫秒
    • int64_t nb_frames;
    • AVRational avg_frame_rate;
    • AVCodecParameters *codecpar:(音视频参数)用于替代过时的1
      • AVBufferRef *buff; //删除的时候-1
      • int64_t pts; //pts * (num/den) 显示时间戳
      • int64_t dts; //解码时间戳
      • unit8_t *data; //压缩编码数据
      • int size; //压缩编码数据大小
      • stream_index //所属的AVStream
      • AVPacket *av_packet_alloc(void); //创建并初始化,申请了堆上的空间,需要自己释放
      • AVPacket *av_packet_clone(const AVPacket *src); //创建和并用计数
      • int av_packet_ref(AVPacket *dst,const AVPacket *src);
      • av_packet_unred(AVPacket *pkt);
      • void av_packet_free(AVPacket **pkt); //清空对象并减引用计数
      • void av_init_packet(AVPacket *pkt); //默认值
      • int av_packet_from_data(AVPacket *pkt,unit7_t *data,int size);
      • int av_copy_packet(AVPacket *dst,const AVPacket *src); ///attribute_deprecated用clone函数代替了
  • av_read_frame(…):从输入文件读取一帧压缩数据

    • AVFormatContext *s;
    • AVPacket *pkt; //不能传null
    • return 0 if OK, < 0 on error or end of file
  • avcodec_decode_video2():解码一帧压缩数据

  • avcodec_close():关闭解码器

找寻

3.PCM过程的一些基本参数

PCM音频数据

4.视频的封装格式及播放的基本流程

视频播放器原理

  • 封装格式(MP4,RMVB,TS,FLV,AVI)
  • 视频编码数据(H.264.MPEG2,VC-1)
  • 音频编码数据(AAC,MP3,AC-3)
  • 视频像素数据(YUV420P,RGB)
  • 音频采样数据(PCM)

播放视频流程

在这里插入图片描述
可以使用软件mediainfo进行读取。

5. ffmpeg的解码大致流程

在这里插入图片描述

FFmpeg解码的流程图如下所示

在这里插入图片描述

6.ffmpeg程序的环境配置

配置ffmpeg所依赖的文件(此处是VS的配置,Qt Creator单独配置):

  • C++ -> 常规 -> 附加包含目录(ffmpeg的include目录)
  • 链接器 -> 附加库目录(debug下面的x64)
  • 调试->工作目录(代码中包含的文件(资料文件,视频文件)所需要的路径)
  • 针对C语言需要多添加一句:
extern "C"{
	#include "libacformat/avformat.h"
}
  • 代码包含或者配置"附加依赖项"(包含lib库)

7.ffmpeg编写解码程序

第一部分

#include <QCoreApplication>
#include <QDebug>
extern "C" {
	#include "libacformat/avformat.h"
}

#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")

static double FractionToDouble(AVRational fraction)//AVRational为分数表示,其目的为避免精度损失,分数表示为分子除以分母
{
	return fraction.den == 0?0:(double)fraction.num/(double)fraction.den;
}

int main(int argc,char *argv[])
{
	QCoreApplication a(argc,argv);
	const char* path = "test.mp4";

	//初始化封装库
	av_register_all();
	//打开输入视频文件
	AVFormatContext *avfc = NULL;
	int ret = avformat_open_input(&avfc,path,NULL,NULL);	//传二级指针一般是对指针进行赋值的时候
	if(ret != 0)
	{
		qDebug()<<"OPen"<<path<<"failed!";
		exit(-1);
	}
	//获取流信息
	ret = avformat_find_stream_info(avfc,NULL);
	if(ret < 0)
	{
		qDebug()<<"avformat_find_stream_info_failed!";
		exit(-1);
	}
	//总时长-秒数
	qDebug()<<(avfc->duration / AV_TIME_BASE);
	//avfc->streams
	qDebug()<<"streams number is:"<<avfc->nb_streams;
	for(int i = 0; i < avfc->nb->streams;i++)
	{
		qDebug()<<"i is "<<i;
		AVStream* as = avfc->streams[i];
		qDebug()<<as->codecpar->codec_id;	//AV_CODEC_ID_H264(解码)
		qDebug()<<as->codecpar->format;
		qDebug();
		
		//音频 AVMEDIA_TYPE_AUDIO
		if(as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			qDebug()<<i<<"audio info";
			qDebug()<<"sample_rate="<<as->codecpar->sample_rate;
			qDebug()<<"Channels="<<as->codecpar->channels;
		}
		//视频 AVMEDIA_TYPE_VIDEO
		else if(as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			qDebug()<<i<<"video info";
			qDebug()<<"width="<<as->codecpar->width;
			qDebug()<<"height="<<as->codecpar->height;
			//帧率fps
			qDebug()<<"video fps="<<FractionToDouble(as->avg_frame_rate);
		}
	}

	return a.exec();	
}

第二部分

#include <QCoreApplication>
#include <QDebug>
extern "C" {
	#include "libacformat/avformat.h"
	#include "libavcodec/avcodec.h"	//解码
}

#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")

static double FractionToDouble(AVRational fraction)
{
	return fraction.den == 0?0:(double)fraction.num/(double)fraction.den;
}

int main(int argc,char *argv[])
{
	QCoreApplication a(argc,argv);
	const char* path = "test.mp4";


	av_register_all();
	
	//初始化解码器
	avcodec_register_all();
	
	//音视频索引,读取时可以区别音视频
	int videoStream = 0;	//
	int audioStream = 1;	//

	AVFormatContext *avfc = NULL;
	int ret = avformat_open_input(&avfc,path,NULL,NULL);
	if(ret != 0)
	{
		qDebug()<<"OPen"<<path<<"failed!";
		exit(-1);
	}

	ret = avformat_find_stream_info(avfc,NULL);
	if(ret < 0)
	{
		qDebug()<<"avformat_find_stream_info_failed!";
		exit(-1);
	}

	qDebug()<<(avfc->duration / AV_TIME_BASE);
	//avfc->streams
	qDebug()<<"streams number is:"<<avfc->nb_streams;
	for(int i = 0; i < avfc->nb->streams;i++)
	{
		qDebug()<<"i is "<<i;
		AVStream* as = avfc->streams[i];
		qDebug()<<as->codecpar->codec_id;
		qDebug()<<as->codecpar->format;
		qDebug();
		

		if(as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;	//
			qDebug()<<"sample_rate="<<as->codecpar->sample_rate;
			qDebug()<<"Channels="<<as->codecpar->channels;
		}
	
		else if(as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;	//
			qDebug()<<"width="<<as->codecpar->width;
			qDebug()<<"height="<<as->codecpar->height;

			qDebug()<<"video fps="<<FractionToDouble(as->avg_frame_rate);
		}
	}
	//找到视频解码器,创建并打开解码器
	AVCodec* vcodec = avcodec_find_decoder(avfc->streams[videoStream]->codecpar->code_id);
	if (vcodec)
	{
		qDebug()<<"video Can't find the video codec";
	}
	//上下文
	AVCodecCOntext* vc = avcodec_alloc_context3(vcodec);
	//参数
	ret = avcodec_parameters_to_context(vc,avfc->streams[videoStream]->codecpar);
	if (ret < 0)
	{
		qDebug()<<"video avcodec_parameters_to_context failed";
	}
	//8线程同时解码
	vc->thread_count = 8;
	avcodec_open2(vc,NULL,NULL);
	if (ret < 0)
	{
		qDebug()<<"video avcodec_open2 failed";
	}



	//找到音频解码器,创建并打开解码器
	AVCodec* acodec = avcodec_find_decoder(avfc->streams[audiooStream]->codecpar->code_id);
	if (acodec)
	{
		qDebug()<<"audio Can't find the video codec";
	}
	//上下文
	AVCodecCOntext* ac = avcodec_alloc_context3(acodec);
	//参数
	ret = avcodec_parameters_to_context(ac,avfc->streams[audiooStream]->codecpar);
	if (ret < 0)
	{
		qDebug()<<"audio avcodec_parameters_to_context failed";
	}
	//8线程同时解码
	ac->thread_count = 8;
	avcodec_open2(ac,NULL,NULL);
	if (ret < 0)
	{
		qDebug()<<"audio accodec_open2 failed";
	}

	//Packet,Frame
	AVPacket* pkt = av_packet_alloc();  //using av_packet_free()
	AVFrame* frame = av_frame_alloc();	//using av_frame_free()
	
	ret = 0;
	while(ret >= 0)
	{
		ret = av_read_frame(avfc,pkt); //一帧帧的读
		qDebug()<<"pkt->size="<<pkt->size;
		qDebug()<<"pkt->pts="<<pkt->pts;
		qDebug()<<"pkt->dts="<<pkt->dts;
		//完成解码操作
		AVCodecContext* cc = NULL;
		if(pkt->stream_index == videoStream)
		{
			qDebug()<<"视频数据";
			cc = vc;
		}else if(pkt->stream_index == audeoStream)
		{
			qDebug()<<"音频数据";
			cc = ac;
		}
		int ret2 = avcodec_send_packet(cc,pkt);//以frame形式输出
		av_packet_unref(pkt);//引用计数-1
		if(ret2 != 0)
		{
			qDebug<<"Send packet failed";
		}

		//接受frame
		while(true)
		{
			//有可能多处接受decode发过来的frame
			int ret3 = avcodec_receive_frame(cc,frame);
			if(ret3 != 0)
			{
				break;
			}
			qDebug<<"recv frame"<<frame->format<<" "<<frame->linesize[0];
		}
	}
	//释放资源
	av_frame_free(&frame);
	av_packet_free(&pkt);
	if(avfc)
	{
		avformat_close_input(&avfc);
	}
	avfc = NULL;

	return a.exec();	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值