热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

1.音视频采样编码的基本原理将模拟信号转化为数字信号,隔段时间进行采样2.ffmpeg的基本情况介绍Fmpeg是领先的多媒体框架,能够解码、编码

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()&#xff1a;解码一帧压缩数据

  • avcodec_close()&#xff1a;关闭解码器


找寻


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的配置&#xff0c;Qt Creator单独配置)&#xff1a;

  • C&#43;&#43; -> 常规 -> 附加包含目录(ffmpeg的include目录)
  • 链接器 -> 附加库目录(debug下面的x64)
  • 调试->工作目录(代码中包含的文件(资料文件&#xff0c;视频文件)所需要的路径)
  • 针对C语言需要多添加一句&#xff1a;

extern "C"{#include "libacformat/avformat.h"
}

  • 代码包含或者配置"附加依赖项"(包含lib库)

7.ffmpeg编写解码程序


第一部分

#include
#include
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为分数表示&#xff0c;其目的为避免精度损失&#xff0c;分数表示为分子除以分母
{return fraction.den &#61;&#61; 0?0:(double)fraction.num/(double)fraction.den;
}int main(int argc,char *argv[])
{QCoreApplication a(argc,argv);const char* path &#61; "test.mp4";//初始化封装库av_register_all();//打开输入视频文件AVFormatContext *avfc &#61; NULL;int ret &#61; avformat_open_input(&avfc,path,NULL,NULL); //传二级指针一般是对指针进行赋值的时候if(ret !&#61; 0){qDebug()<<"OPen"<<path<<"failed!";exit(-1);}//获取流信息ret &#61; avformat_find_stream_info(avfc,NULL);if(ret < 0){qDebug()<<"avformat_find_stream_info_failed!";exit(-1);}//总时长-秒数qDebug()<<(avfc->duration / AV_TIME_BASE);//avfc->streamsqDebug()<<"streams number is:"<<avfc->nb_streams;for(int i &#61; 0; i < avfc->nb->streams;i&#43;&#43;){qDebug()<<"i is "<<i;AVStream* as &#61; avfc->streams[i];qDebug()<<as->codecpar->codec_id; //AV_CODEC_ID_H264(解码)qDebug()<<as->codecpar->format;qDebug();//音频 AVMEDIA_TYPE_AUDIOif(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_AUDIO){qDebug()<<i<<"audio info";qDebug()<<"sample_rate&#61;"<<as->codecpar->sample_rate;qDebug()<<"Channels&#61;"<<as->codecpar->channels;}//视频 AVMEDIA_TYPE_VIDEOelse if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO){qDebug()<<i<<"video info";qDebug()<<"width&#61;"<<as->codecpar->width;qDebug()<<"height&#61;"<<as->codecpar->height;//帧率fpsqDebug()<<"video fps&#61;"<<FractionToDouble(as->avg_frame_rate);}}return a.exec();
}

第二部分

#include
#include
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 &#61;&#61; 0?0:(double)fraction.num/(double)fraction.den;
}int main(int argc,char *argv[])
{QCoreApplication a(argc,argv);const char* path &#61; "test.mp4";av_register_all();//初始化解码器avcodec_register_all();//音视频索引&#xff0c;读取时可以区别音视频int videoStream &#61; 0; //int audioStream &#61; 1; //AVFormatContext *avfc &#61; NULL;int ret &#61; avformat_open_input(&avfc,path,NULL,NULL);if(ret !&#61; 0){qDebug()<<"OPen"<<path<<"failed!";exit(-1);}ret &#61; avformat_find_stream_info(avfc,NULL);if(ret < 0){qDebug()<<"avformat_find_stream_info_failed!";exit(-1);}qDebug()<<(avfc->duration / AV_TIME_BASE);//avfc->streamsqDebug()<<"streams number is:"<<avfc->nb_streams;for(int i &#61; 0; i < avfc->nb->streams;i&#43;&#43;){qDebug()<<"i is "<<i;AVStream* as &#61; avfc->streams[i];qDebug()<<as->codecpar->codec_id;qDebug()<<as->codecpar->format;qDebug();if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_AUDIO){audioStream &#61; i; //qDebug()<<"sample_rate&#61;"<<as->codecpar->sample_rate;qDebug()<<"Channels&#61;"<<as->codecpar->channels;}else if(as->codecpar->codec_type &#61;&#61; AVMEDIA_TYPE_VIDEO){videoStream &#61; i; //qDebug()<<"width&#61;"<<as->codecpar->width;qDebug()<<"height&#61;"<<as->codecpar->height;qDebug()<<"video fps&#61;"<<FractionToDouble(as->avg_frame_rate);}}//找到视频解码器&#xff0c;创建并打开解码器AVCodec* vcodec &#61; avcodec_find_decoder(avfc->streams[videoStream]->codecpar->code_id);if (vcodec){qDebug()<<"video Can&#39;t find the video codec";}//上下文AVCodecCOntext* vc &#61; avcodec_alloc_context3(vcodec);//参数ret &#61; avcodec_parameters_to_context(vc,avfc->streams[videoStream]->codecpar);if (ret < 0){qDebug()<<"video avcodec_parameters_to_context failed";}//8线程同时解码vc->thread_count &#61; 8;avcodec_open2(vc,NULL,NULL);if (ret < 0){qDebug()<<"video avcodec_open2 failed";}//找到音频解码器&#xff0c;创建并打开解码器AVCodec* acodec &#61; avcodec_find_decoder(avfc->streams[audiooStream]->codecpar->code_id);if (acodec){qDebug()<<"audio Can&#39;t find the video codec";}//上下文AVCodecCOntext* ac &#61; avcodec_alloc_context3(acodec);//参数ret &#61; avcodec_parameters_to_context(ac,avfc->streams[audiooStream]->codecpar);if (ret < 0){qDebug()<<"audio avcodec_parameters_to_context failed";}//8线程同时解码ac->thread_count &#61; 8;avcodec_open2(ac,NULL,NULL);if (ret < 0){qDebug()<<"audio accodec_open2 failed";}//Packet,FrameAVPacket* pkt &#61; av_packet_alloc(); //using av_packet_free()AVFrame* frame &#61; av_frame_alloc(); //using av_frame_free()ret &#61; 0;while(ret >&#61; 0){ret &#61; av_read_frame(avfc,pkt); //一帧帧的读qDebug()<<"pkt->size&#61;"<<pkt->size;qDebug()<<"pkt->pts&#61;"<<pkt->pts;qDebug()<<"pkt->dts&#61;"<<pkt->dts;//完成解码操作AVCodecContext* cc &#61; NULL;if(pkt->stream_index &#61;&#61; videoStream){qDebug()<<"视频数据";cc &#61; vc;}else if(pkt->stream_index &#61;&#61; audeoStream){qDebug()<<"音频数据";cc &#61; ac;}int ret2 &#61; avcodec_send_packet(cc,pkt);//以frame形式输出av_packet_unref(pkt);//引用计数-1if(ret2 !&#61; 0){qDebug<<"Send packet failed";}//接受framewhile(true){//有可能多处接受decode发过来的frameint ret3 &#61; avcodec_receive_frame(cc,frame);if(ret3 !&#61; 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 &#61; NULL;return a.exec();
}


推荐阅读
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 单片微机原理P3:80C51外部拓展系统
      外部拓展其实是个相对来说很好玩的章节,可以真正开始用单片机写程序了,比较重要的是外部存储器拓展,81C55拓展,矩阵键盘,动态显示,DAC和ADC。0.IO接口电路概念与存 ... [详细]
  • 本文回顾了作者初次接触Unicode编码时的经历,并详细探讨了ASCII、ANSI、GB2312、UNICODE以及UTF-8和UTF-16编码的区别和应用场景。通过实例分析,帮助读者更好地理解和使用这些编码。 ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • 字节流(InputStream和OutputStream),字节流读写文件,字节流的缓冲区,字节缓冲流
    字节流抽象类InputStream和OutputStream是字节流的顶级父类所有的字节输入流都继承自InputStream,所有的输出流都继承子OutputStreamInput ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • Unity与MySQL连接过程中出现的新挑战及解决方案探析 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 在当前的软件开发领域,Lua 作为一种轻量级脚本语言,在 .NET 生态系统中的应用逐渐受到关注。本文探讨了 Lua 在 .NET 环境下的集成方法及其面临的挑战,包括性能优化、互操作性和生态支持等方面。尽管存在一定的技术障碍,但通过不断的学习和实践,开发者能够克服这些困难,拓展 Lua 在 .NET 中的应用场景。 ... [详细]
  • JVM钩子函数的应用场景详解
    本文详细介绍了JVM钩子函数的多种应用场景,包括正常关闭、异常关闭和强制关闭。通过具体示例和代码演示,帮助读者更好地理解和应用这一机制。适合对Java编程和JVM有一定基础的开发者阅读。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 网络爬虫的规范与限制
    本文探讨了网络爬虫引发的问题及其解决方案,重点介绍了Robots协议的作用和使用方法,旨在为网络爬虫的合理使用提供指导。 ... [详细]
  • WinMain 函数详解及示例
    本文详细介绍了 WinMain 函数的参数及其用途,并提供了一个具体的示例代码来解析 WinMain 函数的实现。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 网站访问全流程解析
    本文详细介绍了从用户在浏览器中输入一个域名(如www.yy.com)到页面完全展示的整个过程,包括DNS解析、TCP连接、请求响应等多个步骤。 ... [详细]
author-avatar
mobiledu2502922617
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有