400-123-4657
行业资讯
您当前的位置: 首页 > 新闻动态 > 行业资讯

解决FFmpeg保存PCM音频到MP4文件报错问题的方法与技巧

时间:2025-03-14

1。序言

使用FFMPEG将音频和视频保存到MP4文件,您会遇到问题,尤其是在视频监视行业中,即监视摄像机设置的音频是PCM/G711A/G711U,而解码后的相应格式为

PCM_S16BE/PCM_ALAW/PCM_MULAW,将此原始音频流保存到MP4文件将导致错误。当调用avformat_write_header编写文件标头时,提示(-22)无效参数,一个非法参数。浏览源代码,您会发现FFMPEG中的MP4软件包不支持PCMA和PCMU,除非手动更改源代码以添加。默认情况下,由MP4封装格式支持的音频格式为AAC和MP3。实际上,MP4文件本身可以支持PCM音频数据。我不知道为什么不添加ffmpeg。尽管可以通过更改源代码来支持它,但我想推荐另一种方法,即致电

当以格式传递avformat_alloc_output_context2时,请填写MOV代替MP4。 MOV的格式更加兼容,文件扩展名仍然为MP4。对应

avformat_alloc_output_context2函数的描述,格式参数可以为空。如果是空的,则将从保存的文件名扩展名称中选择默认值。如果指定,则指定的一个应占上风。

由于它以MOV格式存储在MP4文件中,因此问题是,它会导致不兼容的文件大小或格式吗?一开始,我也很担心。我特别发现来自多个制造商的摄像机专门测试它们,并发现根本没有变化的量。因此,我个人猜测填充MOV只是为了促进跳过检测。 MOV文件可以使用各种编码格式,包括MPEG-4,H.264,MJPEG等。而MP4文件主要由H.264编码。

以上不仅支持264,而且支持265,即MOV格式也支持264+AAC/264+MP3/264+PCM/264+PCM/264+PCM/264+PCM/264+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+ PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM/265+PCM。这样,原始数据最好将原始数据保存到文件中而无需转码和重新编码,从而可以节省大量CPU。写文件基本上不占据CPU,基本上都是磁盘操作,因此性能瓶颈是磁盘读取和写入功能和网络带宽。

2。效应图片

3.经验地址国内站点:国际网站:个人工作:经验地址:提取代码:01JF文件名:bin_video_demo。视频首页:4。功能功能4.1。基本功能支持各种音频和视频文件格式,例如MP3,WAV,MP4,ASF,RM,RMVB,MKV等。支持本地相机设备和本地桌面获取,并支持多个设备和多个屏幕。支持各种视频流格式,例如RTP,RTSP,RTMP,HTTP,UDP等。本地音频和视频文件以及网络音频和视频文件会自动识别文件长度,播放进度,音量,静音状态等。文件可以指定播放位置,调整音量,调整稳定性,设置静音时间2.次数。等等同于缓慢的播放和快速播放。支持开始播放,停止播放,暂停播放并继续播放。支持捕捉屏幕截图,您可以指定文件路径,并在捕捉完成后选择是否会自动显示预览。支持视频存储,手动开始录制并停止录制。一些内核支持暂停记录并继续记录,跳过了不需要记录的部分。支持诸如非意识开关循环播放和自动重新连接之类的机制。提供信号,例如成功的播放,完成的播放,收到的解码图片,收到的拍摄图片,视频尺寸更改和视频状态更改。多线程处理,解码一个线程,而不粘在主接口中。 4.2。特征功能同时支持多个解码内核,包括QMedia内核(QT4/QT5/QT6),FFMPEG内核(FFMPEG2/FFMPEG2/FFMPEG3/FFMPEG3/FFMPEG4/FFMPEG4/FFMPEG5/FFMPEG6),FFMPEG6),FFMPEG6),VLC KERNEL(VLC2/VLC2/MP2 KERNEL(MP2),MP2/MP2 KERN,MP2/MP2 KERN,MP2 KERN(MP2),MM MMMMM MDK内核,Hikvision SDK,EasyPlayer内核等。

一个非常完整的多基础设计,一种新的解码内核只需要非常少量的代码即可应用整个机制,这很容易扩展。同时,支持多个屏幕显示策略,自动调整(原始分辨率小于显示控制尺寸,并且原始分辨率将根据原始分辨率显示,否则将相等的自由基缩放),相等的自由基缩放(越来越相等的缩放量表),以及拉伸填充(越来越激烈的填充)。所有内核和所有视频显示模式都支持三种屏幕显示策略。同时,支持多个视频显示模式,例如手柄模式(将传入的控制手柄传递给另一方以绘制控制模式),绘图模式(在回调获取数据后,将其转换为qimage和qpainter以qpu模式绘制)和GPU模式(在回调后,将其转换为yuv和yuv yuv and yuv and yuv and with yuv and qopeng gopeng gendget)。支持各种硬件加速类型。可以选择FFMPEG作为DXVA2,D3D11VA等,VLC可以选择为ANY,DXVA2,DXVA2,D3D11VA,MPV可以作为Auto,DXVA2,D3D11VA,D3D11VA,MDK,MDK可以选择为DXVA2,DXVA2,d3Dd11va,cuda,system lin,例如不同的类型。 VDPAU和MACOS系统具有录像带。解码线和显示式形式分开,任何解码内核都可以安装到任何显示表单上并动态开关。它支持共享的解码线程,默认情况下会打开并自动处理。当确定相同的视频地址并共享一个解码线程时,它可以在网络视频环境中极大地节省网络流量和另一方设备的推动压力。

所有顶级国内视频制造商都采用了此策略。这样,只需将视频流绘制,您就可以将其分享到数十个或数百个频道以显示它。自动识别视频的旋转角并绘制它。例如,在手机上拍摄的视频通常旋转90度,并且必须在播放期间自动旋转,否则默认情况下会逆转。自动识别视频流播放期间分辨率的变化,并自动调整视频控件上的大小。例如,摄像机可以在使用过程中动态配置分辨率,并且相应的视频控件还必须在更改分辨率后进行同步反应。音频和视频文件将在没有感知的情况下自动切换和播放循环,并且在切换期内,肉眼(例如黑屏)看不到切换轨迹。视频控件还支持任何解码内核,任何屏幕显示策略和任何视频显示模式。视频控制浮子杆支持三种模式:手柄,绘制和GPU,并移动了非吸毒坐标。本地相机设备支持指定的设备名称,分辨率和播放帧速率。本地桌面采集支持设置采集区域,偏移值,指定的桌面索引,框架速率,同时获得多个台式机等。它还支持用于选择窗口标题的固定窗口。视频文件还支持开放视频文件,本地摄像机,本地桌面,网络视频流等。即时响应打开和关闭,无论是打开不存在的视频还是网络流,都可以检测设备是否存在,在阅读过程中等待超时,并在接收关闭命令时立即中断先前的操作并响应。支持打开各种图片文件,并支持本地音频和视频文件的拖放和播放。视频流通信方法可以选择为TCP/UDP。某些设备可能只提供特定的协议通信,例如TCP,并且需要指定此协议以打开。

您可以设置连接超时时间(用于视频流检测的超时时间)和读取超时时间(获取期间的超时时间)。支持按框架播放的框架,提供上一个帧/下一个帧函数接口,并可以查看收集的逐帧图像。音频文件会自动提取专辑信息,例如标题,艺术家,专辑,专辑封面,并自动显示专辑封面。视频响应的延迟极低约为0.2,并且开放视频流的快速响应约为0.5s,并且进行了特殊的优化处理。支持H264/H265编码(现在越来越多的监视摄像机以H265视频流格式)生成视频文件,并自动识别并切换编码格式。支持在用户信息中包含特殊字符的视频流(例如 +#@和用户信息中的其他字符),并且是内置的解析和逃生处理。它支持过滤器,各种水印和图形效果,支持多个水印和图像,并且可以为MP4文件编写OSD标签信息以及各种图形信息。它支持视频流中的各种音频格式,包括AAC,PCM,G.726,G.711A,G.711MU,G.711ulaw,G.711Alaw,MP2L2等,建议选择具有最佳交叉图形的AAC兼容性。内核FFMPEG使用纯QT+FFMPEG解码,并且不依赖第三方图纸和播放(例如SDL)。 GPU图形使用Qopenglwidget,音频播放使用QaudioOutput。内核FFMPEG和内核MDK支持Android,其中MDK支持Android Hard Decoding,其性能非常残酷。

您可以切换音频和视频轨道,即程序频道。 TS文件可能包含多个音频和视频程序流。您可以设置要播放的一个,并且可以在播放前和在播放过程中动态设置。可以设置视频旋转角度,并且可以在播放前设置,并在播放过程中动态更改。视频控制浮子带有诸如启动和停止录制切换,语音静音切换,捕捉屏幕截图以及关闭视频之类的功能。音频组件支持声音波形值数据的分析,并且可以根据此值绘制波形曲线和柱状声音条,默认情况下提供声音振幅信号。标签和图形信息支持三种绘图方法:绘制到掩码层,绘制到图片和从源图绘制(相应的信息可以存储在文件中)。通过传递URL地址,该地址可以配备通信协议,解决方案,帧速率和其他信息,而无需其他设置。将视频保存到文件支持三种策略:自动处理,仅文件和所有转码。转码策略支持自动标识264和265。编码保存支持指定的分辨率缩放或等级缩放。例如,如果您有保存文件量的要求,则可以指定缩放并将其存储。支持播放文件的加密和解密,并可以指定密钥文本。监视布局类提供了64个频道和同时显示,还支持各种特殊布局,例如手机上的13个频道和6行和2列布局。可以自由定义各种布局。支持电子扩增。切换到浮栏上的电子放大模式。选择要在屏幕上放大的区域。选择后,它将自动放大。再次切换放大模式重置。

每个组件中都有非常详细的打印信息提示,尤其是错误消息提示和包装的统一打印格式。在复杂的设备环境上进行测试非常方便且有用,这相当于准确定位哪个通道以及哪个步骤是错误的。同时,它提供了简单的示例,视频播放器,多屏幕视频监视,监视播放,逐帧播放,多屏幕渲染和其他单独的表单示例,专门展示了如何使用相应的功能。可以从不同的制造商类型,播放时间段,用户信息和指定的频道中选择监视和播放。支持切换播放进度。您可以从声卡设备下拉框中选择声卡以播放声音,以提供相应的开关声卡功能接口。它支持移动应用程序的汇编,并提供了一种特殊的移动应用程序布局界面,该界面可用作手机上的视频监视。代码框架和结构最佳,具有强大的性能,详细的注释以及连续的迭代,更新和升级。源代码支持Windows,Linux,Mac,Android等,并支持各种家用Linux系统,包括但不限于Tongxin UOS/Zhongbiao Kirin/Galaxy Kirin等。还支持嵌入的Linux。源代码支持QT4,QT5,QT6,并且与所有版本兼容。 4.3。视频控件可以动态添加尽可能多的OSD标签信息,包括名称,是否可见,字体大小,文本字符,文本颜色,背景颜色,标签图片,标签坐标,标签格式(文本,日期,日期,时间,时间,日期和时间,图片,标签),标签位置(左上角,左下角,左下角,左下角,右上角,右上角,右角,右角,自定义坐标)。

它可以动态添加尽可能多的图形信息,例如,可以直接发送到视频控件中的人工智能算法所解析的图形区域信息。图形信息支持任何形状,使用绝对坐标直接在原始图片上绘制。图形信息包括名称,边框大小,边框颜色,背景颜色,矩形区域,路径收集,点坐标集合等。每个图形信息都可以在三个区域中的一个或多个区域中指定,并绘制指定的图形信息。内置的浮杆控件,浮动条位置支持顶部,底部,左和右侧。浮带控件的参数包括边距,间距,背景透明度,背景颜色,文本颜色,颜色,位置,按钮图标代码收集,按钮名称标识集合以及按钮提示信息收集。浮带控件可以在一排工具按钮中自定义。设置了结构参数,可以选择图标作为图形字体或自定义图片。浮栏按钮实现了诸如录制开关,屏幕截图,静音切换和关闭视频之类的功能。您还可以自己将自己的相应功能添加到源代码。浮栏按钮对应于实现该功能的按钮,并具有相应的图标切换处理。例如,按下录制按钮后,它将切换到录音中的图标,然后切换声音按钮后,它将成为静音图标,然后切换以再次还原。单击“浮栏”按钮后,名称的唯一名称将作为信号发送,并且可以单独处理响应。浮动空白区域可以显示及时的信息,默认情况下可以显示当前视频分辨率的大小,并且可以增加帧速率,代码流大小和其他信息。 The parameters of the video control include border size, border color, focus color, background color (default transparent), text color (default global text color), fill color (fill black in the blank space outside the video), background text, background picture (if the picture is set priority), whether to copy the picture, zoom display mode (automatic adjustment, equal ratio zoom, stretch filling), video display mode (handle, draw, GPU), enable floating bar, floating bar size (水平高度,垂直宽度),浮栏位置(顶部,底部,左,右)。 5。相关代码

bool FFmpegSaveHelper::rtmp_pcm = false;
QStringList FFmpegSaveHelper::vnames_file = QStringList() << "h264" << "hevc";
QStringList FFmpegSaveHelper::anames_pcm = QStringList() << "pcm_mulaw" << "pcm_alaw" << "pcm_s16be";
QStringList FFmpegSaveHelper::anames_file = QStringList() << "aac" << "mp2" << "mp3" << "ac3" << anames_pcm;
QStringList FFmpegSaveHelper::anames_rtmp = QStringList() << "aac" << "mp3";
QStringList FFmpegSaveHelper::anames_rtsp = QStringList() << "aac" << "mp3" << anames_pcm void ffmpegsavehelper::checkencodeffmpegsave thread const qstring videocodecname const qstring audiocodecname bool videoencode bool audioencode encodeaudio encodeaudio bool needaudio bool notsupportvideo='false;' bool notsupportaudio='false;' savemode savemode='thread-'>getSaveMode();
    QString mediaUrl = thread->property("mediaUrl").toString();
    if (saveMode == SaveMode_File) {
        notSupportVideo = !vnames_file.contains(videoCodecName);
        notSupportAudio = !anames_file.contains(audioCodecName);
    } else {
        //具体需要根据实际需求进行调整
        if (saveMode == SaveMode_Rtmp) {
            notSupportVideo = (videoCodecName != "h264");
            notSupportAudio = !anames_rtmp.contains(audioCodecName);
        } else if (saveMode == SaveMode_Rtsp) {
            notSupportVideo = !vnames_file.contains(videoCodecName);
            notSupportAudio = !anames_rtsp.contains(audioCodecName);
        }
        //特定格式过滤
        if (mediaUrl.endsWith(".m3u8")) {
            notSupportAudio = true;
        }
    }
    if (notSupportVideo) {
        thread->debug(0, "视频格式", QString("警告: %1").arg(videoCodecName));
        videoEncode = true;
    }
    if (notSupportAudio) {
        thread->debug(0, "音频格式", QString("警告: %1").arg(audioCodecName));
        audioEncode = true;
    }
    //0. 因为还没有搞定万能转换/所以暂时做下面的限制
    //1. 保存文件模式下纯音频统一编码成pcma
    //2. 保存文件模式下视音频且启用了转码则禁用音频
    //3. 推流RTMP模式下启用了转码则禁用音频
    //4. 推流RTSP模式下纯音频且启用了转码则编码成pcma
    //5. 推流RTSP模式下启用了转码则禁用音频
    //6. 纯音频aac格式在推流的时候可选转码/有些流媒体程序必须要求转码才能用
    bool encodeAac = false;
    bool onlySaveAudio = thread->getOnlySaveAudio();
    bool onlyAac = (onlySaveAudio && audioCodecName == "aac");
    if (encodeAudio == EncodeAudio_Auto) {
        if (saveMode == SaveMode_File) {
            if (onlySaveAudio || audioCodecName == "pcm_s16le") {
                encodeAudio = EncodeAudio_Pcma;
            } else if (audioEncode) {
                needAudio = false;
            }
        } else if (saveMode == SaveMode_Rtmp) {
            if (audioEncode) {
                needAudio = false;
            } else if (onlyAac && encodeAac) {
                encodeAudio = EncodeAudio_Aac;
            }
        } else if (saveMode == SaveMode_Rtsp) {
            if (audioEncode) {
                encodeAudio = EncodeAudio_Pcma;
            } else if (onlyAac && encodeAac) {
                encodeAudio = EncodeAudio_Pcma;
            }
        }
    }
    //如果设置过需要检查B帧/有B帧推流需要转码/否则一卡卡
    if (!videoEncode && !onlySaveAudio && saveMode != SaveMode_File) {
        bool checkB = thread->property("checkB").toBool();
        bool isFile = thread->property("isFile").toBool();
        if (checkB && isFile && FFmpegUtil::hasB(mediaUrl)) {
            videoEncode = true;
        }
    }
    //部分流媒体服务支持推pcma和pcmu
    if (rtmp_pcm && saveMode == SaveMode_Rtmp && anames_pcm.contains(audioCodecName)) {
        needAudio = true;
        encodeAudio = EncodeAudio_Pcma;
    }
    //音频需要强转则必须设置启用音频编码
    if (encodeAudio != EncodeAudio_Auto) {
        audioEncode = true;
    }
}
const char *FFmpegSaveHelper::getFormat(AVDictionary **options, QString &fileName, bool mov, const QString &flag)
{
    //默认是mp4/mov更具兼容性比如音频支持pcma等
    const char *format = mov ? "mov" : "mp4";
    if (fileName.startsWith("rtmp://")) {
        format = "flv";
    } else if (fileName.startsWith("rtsp://")) {
        format = "rtsp";
        av_dict_set(options, "stimeout", "3000000", 0);
        av_dict_set(options, "rtsp_transport", "tcp", 0);
    } else if (fileName.startsWith("udp://")) {
        format = "mpegts";
    } else {
        QByteArray temp;
        if (!flag.isEmpty()) {
            temp = flag.toUtf8();
            format = temp.constData();
            QString suffix = fileName.split(".").last();
            fileName.replace(suffix, flag);
        }
    }
    return format;
}
bool FFmpegSave::initStream()
{
    //如果存在秘钥则启用加密
    AVDictionary *options = NULL;
    FFmpegHelper::initEncryption(&options, this->property("cryptoKey").toByteArray());
    QString flag;
    if (getOnlySaveAudio() && encodeAudio != EncodeAudio_Aac) {
        flag = "wav";
    }
    //既可以是保存到文件也可以是推流(对应格式要区分)
    bool mov = audioCodecName.startsWith("pcm_");
    const char *format = FFmpegSaveHelper::getFormat(&options, fileName, mov, flag);
    //开辟一个格式上下文用来处理视频流输出(末尾url不填则rtsp推流失败)
    QByteArray fileData = fileName.toUtf8();
    const char *url = fileData.data();
    int result = avformat_alloc_output_context2(&formatCtx, NULL, format, url);
    if (result < 0 debugresult return false if this->initVideoStream()) {
        goto end;
    }
    //创建输出音频流
    if (!this->initAudioStream()) {
        goto end;
    }
    //打开输出文件
    if (!(formatCtx->oformat->flags & AVFMT_NOFILE)) {
        //记录开始时间并设置回调用于超时判断
        startTime = av_gettime();
        formatCtx->interrupt_callback.callback = FFmpegSaveHelper::openAndWriteCallBack;
        formatCtx->interrupt_callback.opaque = this;
        tryOpen = true;
        result = avio_open2(&formatCtx->pb, url, AVIO_FLAG_WRITE, &formatCtx->interrupt_callback, NULL);
        tryOpen = false;
        if (result < 0) {
            debug(result, "打开输出", "");
            goto end;
        }
    }
    //写入文件开始符
    result = avformat_write_header(formatCtx, &options);
    if (result < 0 debugresult goto end writeheader='true;' debug0 qstring: 1.argformat return true end: this->close();
    this->deleteFile(fileName);
    return false;
}
bool FFmpegSave::initVideoStream()
{
    if (needVideo) {
        videoIndexOut = 0;
        AVStream *stream = avformat_new_stream(formatCtx, NULL);
        if (!stream) {
            return false;
        }
        //设置旋转角度(没有编码的数据是源头带有旋转角度的/编码后的是正常旋转好的)
        if (!videoEncode) {
            FFmpegHelper::setRotate(stream, rotate);
        }
        //复制解码器上下文参数(不编码从源头流拷贝/编码从设置的编码器拷贝)
        int result = -1;
        if (videoEncode) {
            stream->r_frame_rate = videoCodecCtx->framerate;
            result = FFmpegHelper::copyContext(videoCodecCtx, stream, true);
        } else {
            result = FFmpegHelper::copyContext(videoStreamIn, stream);
        }
        if (result < 0) {
            debug(result, "复制参数", "");
            return false;
        }
    }
    return true;
}
bool FFmpegSave::initAudioStream()
{
    if (needAudio) {
        audioIndexOut = (videoIndexOut == 0 ? 1 : 0);
        AVStream *stream = avformat_new_stream(formatCtx, NULL);
        if (!stream) {
            return false;
        }
        //复制解码器上下文参数(不编码从源头流拷贝/编码从设置的编码器拷贝)
        int result = -1;
        if (audioEncode) {
            result = FFmpegHelper::copyContext(audioCodecCtx, stream, true);
        } else {
            result = FFmpegHelper::copyContext(audioStreamIn, stream);
        }
        if (result < 0) {
            debug(result, "复制参数", "");
            return false;
        }
    }
    return true;
}

地址:广东省广州市天河区88号   电话:400-123-4657   传真:+86-123-4567
版权所有:Copyright © 2002-2025 完美真人网站 版权所有 非商用版本      ICP备案编号:粤IP**********
完美真人·(中国APP)官方网站