windows平台ffmpeg学习笔记(二)

简介: 笔记

六、音频转码


本节将PCM转为AAC格式。再来复习一下FFmpeg的工作流程:

60.png


本次demo也是基于保存网络流到本地的基础上的,可以看出,ffmpeg的使用流程是相当固定的。

做出如下修改:

  1. 输入为麦克风,获取PCM数据,输入路径从设备管理器可以查看:61.png

#include <dshow>
//字符转码
static char *dup_wchar_to_utf8(const wchar_t *w)
{
    char *s = NULL;
    int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
    s = (char *)av_malloc(l);
    if (s)
        WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
    return s;
}
//设置输入路径
//避免找不到中文符号
string fileAudioInput = dup_wchar_to_utf8(L"麦克风阵列=Realtek High Definition Audio");
int ret = OpenInput(fileAudioInput);

2. 初始化的方法中添加设备注册

//ffmpeg初始化
void  Init() {
    av_register_all();
    avcodec_register_all();
    avfilter_register_all();
    avformat_network_init();
    avdevice_register_all();//设备注册
    av_log_set_level(AV_LOG_ERROR);
}

3. 输出文件修改

OpenOutput("C:/Users/bian/Desktop/aac.ts");

4. 初始化CodecFilter,Codec编码器等,代码太多,不过多描述

下面附上github源码地址

做了几个简单的demo程序了,感觉好多方法不知道参数的意义,没有入门理解ffmpeg,后面要了解一引起基本原理了。


七、Ffmpeg重要数据结构


本章简单探索一下ffmpeg的一些数据结构,这样在后面的学习过程中可以有自己的思考,更能深入得理解为什么要这样写,有新的需求该如何写。

重点学习以下几个结构体:

  1. AVFormatContext
  2. AVFrame
  3. AVPacket


八、搭建简单直播系统


直播架构图:

62.png

其实在讲转码的时候已经学习过了。下面学习一下使用wireshark抓取rtp包来分析。

首先安装WireShark和npcap。

打开WireShark,选择本地连接即可。

设置使用UDP还是TCP传输:

AVDictionary *options = nullptr;
//参数设置使用UDP传输
av_dict_set(&options, "rstp_transport", "udp", 0);
int ret = avformat_open_input(&inputContext, inputUrl.c_str(), nullptr, &options);


八、音频裁剪

使用音频裁剪的流程与前面的工程类似,初始化,打开输入流,打开输出流,不同的地方在于把指定要裁剪的数据写入到输出文件。

理解本项目的关键在于理解AVPacketAVRational的数据结构,尤其是pts和dts的理解,下面简单讲一下这两个数据结构的关键内容。

AVPacket的dts和pts

FFmpeg里有两种时间戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顾名思义,前者是解码的时间,后者是显示的时间。要仔细理解这两个概念,需要先了解FFmpeg中的packet和frame的概念。

FFmpeg中用AVPacket结构体来描述解码前或编码后的压缩包,用AVFrame结构体来描述解码后或编码前的信号帧。 对于视频来说,AVFrame就是视频的一帧图像。这帧图像什么时候显示给用户,就取决于它的PTS。DTS是AVPacket里的一个成员,表示这个压缩包应该什么时候被解码。 如果视频里各帧的编码是按输入顺序(也就是显示顺序)依次进行的,那么解码和显示时间应该是一致的。可事实上,在大多数编解码标准(如H.264或HEVC)中,编码顺序和输入顺序并不一致,于是才会需要PTS和DTS这两种不同的时间戳。

每个AVPacket的时间戳间隔是固定的,那么这个间隔如何计算呢?答案是denominator/帧率,那时间戳间隔如何用秒或者微秒表示呢?

在FFMPEG中有三种时间单位:秒、微秒和dts/pts。从dts/pts转化为微秒公式:

dts* AV_TIME_BASE/ denominator

其中AV_TIME_BASE为1,000,000,denominator为90,000。

现在就更好的理解了,denominator其实就是把一秒等分的个数。例如帧率为30,也就是一秒30帧,denominator为90000,一帧其实是90000/30=3000个,即(1秒/90000)*3000。

denominator是AVRational结构体变量:

typedef struct AVRational{
    int num; ///< Numerator
    int den; ///< Denominator
} AVRational;

同时根据上面所述,dts是某一帧的解码时间,pts是显示时间,那么pts肯定要大于dts的,因为要先解码才能播放吧,这一点一定要弄清楚。

好下,下面上主程序:

int main()
{
    //定义裁剪位置,1)按包个数来裁剪,对应时间与帧率有关;2)根据时间戳裁剪
    int startPacketNum = 200;   //开始裁剪位置
    int discardPacketNum = 200; //裁剪的包个数
    int count = 0;
    //记录startPacketNum最后一个包的pts和dts,本例中的视频源pts和dts是不同的
    int64_t lastPacketPts = AV_NOPTS_VALUE;
    int64_t lastPacketDts = AV_NOPTS_VALUE;
    //时间戳间隔,不同的视频源可能不同,我的是3000
    int timerIntervel = 3000;
    int64_t lastPts = AV_NOPTS_VALUE;
    Init();
    //只支持视频流,不能带有音频流
    int ret = OpenInput("gaoxiao-v.mp4");//视频流,注意输入如果带有音频会失败,导致音频与视频不同步
    if (ret >= 0) {
        ret = OpenOutput("gaoxiao-v-caijian.mp4");
    }
    if (ret < 0) goto Error;
    while (true) {
        count++;
        auto packet = ReadPacketFromSource();
        if (packet) {
            if (count <= startPacketNum || count > startPacketNum + discardPacketNum) {
                if (count >= startPacketNum + discardPacketNum) {
                    //需要调整dts和pts,调整策略和视频源的pts和dts的规律有关,不是固定的
                    packet->dts = -6000 + (count - 1 - discardPacketNum) * timerIntervel;
                    if (count % 4 == 0) {
                        packet->pts = packet->dts;
                    }
                    else if (count % 4 == 1) {
                        packet->pts = packet->dts + 3000;
                    }
                    else if (count % 4 == 2) {
                        packet->pts = packet->dts + 15000;
                    }
                    else if (count % 4 == 3) {
                        packet->pts = packet->dts + 6000;
                    }
                }
                ret = WritePacket(packet);
            }
        }
        else {
            break;
        }
    }
    cout << "cut file end\n" << endl;
Error:
    CloseInput();
    CloseOutput();
...
    return 0;
}

关于上面代码中不理解的地方可能就是调整dts和pts的策略了,注释中也说明了,这跟视频的dts和pts的策略有关,我为什么这样写呢?下面我打印了原始视频的dts和pts

pakcet.pts=0,pakcet.dts=-6000  //第一个packet,count=1
pakcet.pts=12000,pakcet.dts=-3000   //count=2
pakcet.pts=6000,pakcet.dts=0  //count=3
pakcet.pts=3000,pakcet.dts=3000  //count=4
pakcet.pts=9000,pakcet.dts=6000  //count=5
pakcet.pts=24000,pakcet.dts=9000  //count=6
pakcet.pts=18000,pakcet.dts=12000  //count=7
pakcet.pts=15000,pakcet.dts=15000  //count=8
pakcet.pts=21000,pakcet.dts=18000
pakcet.pts=36000,pakcet.dts=21000
pakcet.pts=30000,pakcet.dts=24000
pakcet.pts=27000,pakcet.dts=27000
pakcet.pts=33000,pakcet.dts=30000
pakcet.pts=48000,pakcet.dts=33000
pakcet.pts=42000,pakcet.dts=36000
pakcet.pts=39000,pakcet.dts=39000
pakcet.pts=45000,pakcet.dts=42000
pakcet.pts=60000,pakcet.dts=45000
pakcet.pts=54000,pakcet.dts=48000
pakcet.pts=51000,pakcet.dts=51000
pakcet.pts=57000,pakcet.dts=54000
pakcet.pts=72000,pakcet.dts=57000
pakcet.pts=66000,pakcet.dts=60000
pakcet.pts=63000,pakcet.dts=63000
...

从上面的Log中可以看到dts是非常有规律的,起始为-6000,按等差3000递增。而pts看着似乎有点乱,但是有有一定的规律,就是在特定packet包ptd=dts,这个packet的个数为4的整倍数,而不是4的整倍数的也很容易找出规律,模4余1时,pts=dts+3000,余2时pts=dts+15000,余3时pts=dts+6000。这样就OK了。

目录
相关文章
|
5天前
|
Python Windows
在 Windows 平台下打包 Python 多进程代码为 exe 文件的问题及解决方案
在使用 Python 进行多进程编程时,在 Windows 平台下可能会出现将代码打包为 exe 文件后无法正常运行的问题。这个问题主要是由于在 Windows 下创建新的进程需要复制父进程的内存空间,而 Python 多进程机制需要先完成父进程的初始化阶段后才能启动子进程,所以在这个过程中可能会出现错误。此外,由于没有显式导入 Python 解释器,也会导致 Python 解释器无法正常工作。为了解决这个问题,我们可以使用函数。
13 5
|
8天前
|
编解码 Windows
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
XviD是开源MPEG-4视频编码器,与DivX相似但后者非开源。早期MP4常使用XviD或DivX编码,现已被H.264取代。在Windows上集成FFmpeg的XviD编解码库libxvid,需访问<https://labs.xvid.com/source/>下载源码,解压后在MSYS环境中配置、编译和安装。之后重新配置FFmpeg,启用libxvid并编译安装。详细步骤包括configure命令、make和make install。成功后,通过`ffmpeg -version`检查是否启用libxvid。更多音视频开发技术可参考《FFmpeg开发实战:从零基础到短视频上线》。
36 0
FFmpeg开发笔记(二十九)Windows环境给FFmpeg集成libxvid
|
1月前
|
算法 Linux Windows
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
在Windows环境下为FFmpeg集成字幕渲染库libass涉及多个步骤,包括安装freetype、libxml2、gperf、fontconfig、fribidi、harfbuzz和libass。每个库的安装都需要下载源码、配置、编译和安装,并更新PKG_CONFIG_PATH环境变量。最后,重新配置并编译FFmpeg以启用libass及相关依赖。完成上述步骤后,通过`ffmpeg -version`确认libass已成功集成。
46 1
FFmpeg开发笔记(十七)Windows环境给FFmpeg集成字幕库libass
|
29天前
|
编解码 5G Linux
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
AVS3是中国首个8K及5G视频编码标准,相比AVS2和HEVC性能提升约30%。解码器libuavs3d支持8K/60P视频实时解码,兼容多种平台。《FFmpeg开发实战》书中介绍了在Windows环境下如何集成libuavs3d到FFmpeg。集成步骤包括下载源码、使用Visual Studio 2022编译、调整配置、安装库文件和头文件,以及重新配置和编译FFmpeg以启用libuavs3d。
42 0
FFmpeg开发笔记(二十一)Windows环境给FFmpeg集成AVS3解码器
|
1月前
|
数据库 Windows
第五十章 使用 ^SystemPerformance 监视性能 - Microsoft Windows 平台的 InterSystems IRIS 性能数据报告
第五十章 使用 ^SystemPerformance 监视性能 - Microsoft Windows 平台的 InterSystems IRIS 性能数据报告
26 0
|
1月前
|
编解码 Linux Windows
FFmpeg开发笔记(十三)Windows环境给FFmpeg集成libopus和libvpx
本文档介绍了在Windows环境下如何为FFmpeg集成libopus和libvpx库。首先,详细阐述了安装libopus的步骤,包括下载源码、配置、编译和安装,并更新环境变量。接着,同样详细说明了libvpx的安装过程,注意需启用--enable-pic选项以避免编译错误。最后,介绍了重新配置并编译FFmpeg以启用这两个库,通过`ffmpeg -version`检查是否成功集成。整个过程参照了《FFmpeg开发实战:从零基础到短视频上线》一书的相关章节。
64 0
FFmpeg开发笔记(十三)Windows环境给FFmpeg集成libopus和libvpx
|
1月前
|
Windows
LabVIEW在Windows平台上的图形导入
LabVIEW在Windows平台上的图形导入
27 0
|
13天前
|
编解码 安全 网络安全
RealVNC的 VNC server在windows7系统下无法正确运行
在Windows 7上运行旧版VNC Server(如4.1.2)可能存在兼容性问题,但可通过调整配置解决。步骤包括:安装VNC Server,设置兼容性模式(选择Windows XP SP3),启动VNC Server,配置VNC连接参数。若遇到问题,检查防火墙设置,确保系统更新,并考虑升级到新版VNC Server以提高性能和兼容性。
|
26天前
|
开发框架 .NET API
在Windows Server 2008 R2上运行.Net 8应用
在Windows Server 2008 R2上成功运行.Net 8程序,需安装三个补丁:Windows Server 2008 R2 SP1 (KB976932)是基础更新;VC_redist.x64提供MSVC库支持;KB3063858解决.NET运行时加载`kernel.dll`的路径问题。KB3063858可能需要KB2533623。详细信息和下载链接在文中给出。