1. 程式人生 > 實用技巧 >Qt音視訊開發11-ffmpeg常用命令

Qt音視訊開發11-ffmpeg常用命令

一、前言

大部分的格式轉換工具比如格式化工廠等,都用到了ffmpeg來處理,ffmpeg編譯後生成的ffmpeg.exe、ffplay.exe、ffprobe.exe等可執行檔案,其實就封裝了眾多牛逼的功能,ffprobe檢視媒體檔案頭資訊的工具,ffplay用於播放媒體檔案的工具,尤其是ffmpeg.exe,強大的媒體檔案轉換工具,可以轉換任何媒體檔案,還可以用自己的 AudioFilter 以及 VideoFliter 進行處理和編輯,比如下面的一些功能。

  1. 列出支援的格式:ffmpeg -formats
  2. 剪下一段媒體檔案:ffmpeg -i input.mp4 -ss 00:00:50.0 -codec copy -t 20 output.mp4
  3. 提取一個視訊檔案中的音訊檔案:ffmpeg -i input.mp4 -vn -acodec copy output.m4a
  4. 視訊靜音,即只保留視訊:ffmpeg -i input.mp4 -an -vcodec copy output.mp4
  5. 從MP4檔案中抽取視訊流匯出為裸的H264資料:ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264
  6. 使用AAC音訊資料和H264視訊生成MP4檔案:ffmpeg -i test.aac -i test.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4
  7. 音訊格式轉換:ffmpeg -i input.wav -acodec libfdk_aac output.aac
  8. 將一個MP4的檔案轉換為一個GIF動圖:ffmpeg -i input.mp4 -vf scale=100:-1 -t 5 -r 10 image.gif

既然這些工具這麼牛逼,那是不是可以考慮做一個功能,直接程式呼叫這些可執行檔案做處理呢?當然,QProcess上場了,他可以直接呼叫可執行程式或者直接執行命令,然後能夠攔截輸出列印的資訊,管道的形式read出來,這樣就非常直觀了,可以在呼叫可執行檔案執行的時候,將列印資訊全部輸出。

二、功能特點

  1. 多執行緒實時播放視訊流+本地視訊+USB攝像頭等。
  2. 支援windows+linux+mac,支援ffmpeg3和ffmpeg4,支援32位和64位。
  3. 多執行緒顯示影象,不卡主介面。
  4. 自動重連網路攝像頭。
  5. 可設定邊框大小即偏移量和邊框顏色。
  6. 可設定是否繪製OSD標籤即標籤文字或圖片和標籤位置。
  7. 可設定兩種OSD位置和風格。
  8. 可設定是否儲存到檔案以及檔名。
  9. 可直接拖曳檔案到ffmpegwidget控制元件播放。
  10. 支援h265視訊流+rtmp等常見視訊流。
  11. 可暫停播放和繼續播放。
  12. 支援儲存單個視訊檔案和定時儲存視訊檔案。
  13. 自定義頂部懸浮條,傳送單擊訊號通知,可設定是否啟用。
  14. 可設定畫面拉伸填充或者等比例填充。
  15. 可設定解碼是速度優先、質量優先、均衡處理。
  16. 可對視訊進行截圖(原始圖片)和截圖。
  17. 錄影檔案儲存支援裸流和MP4檔案。
  18. 支援qsv、dxva2、d3d11va等硬解碼。
  19. 支援opengl繪製視訊資料,極低CPU佔用。
  20. 支援嵌入式linux,交叉編譯即可。

三、效果圖

四、相關站點

  1. 國內站點:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 國際站點:https://github.com/feiyangqingyun/QWidgetDemo
  3. 個人主頁:https://blog.csdn.net/feiyangqingyun
  4. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/
  5. 體驗地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心程式碼

FFmpegTool::FFmpegTool(QObject *parent) : QObject(parent)
{
    //繫結訊號槽
    connect(&process, SIGNAL(started()), this, SIGNAL(started()));
    connect(&process, SIGNAL(finished(int)), this, SIGNAL(finished()));
    connect(&process, SIGNAL(readyReadStandardOutput()), this, SLOT(readData()));
    process.setProcessChannelMode(QProcess::MergedChannels);
}

void FFmpegTool::readData()
{
    QString data = process.readAllStandardOutput();
    emit receiveData(data);
}

void FFmpegTool::start(const QString &command)
{
    process.start(command);
}

void FFmpegTool::start(const QString &program, const QStringList &arguments)
{
    process.start(program, arguments);
}

void FFmpegTool::getMediaInfo(const QString &mediaFile, bool json)
{
    //ffprobe -print_format json -show_streams d:/out.mp4
    //不同平臺可執行檔案路徑改成自己的
    QString jsonArg = "-print_format json -show_streams";
    QString binFile = qApp->applicationDirPath() + "/ffprobe.exe";
    QString cmd = QString("%1 %2 %3").arg(binFile).arg(json ? jsonArg : "").arg(mediaFile);
    start(cmd);
}

void FFmpegTool::h264ToMp4ByCmd(const QString &h264File, const QString &aacFile, const QString &mp4File)
{
    if (!QFile(h264File).exists() || mp4File.isEmpty()) {
        return;
    }

    //具體引數可以參考 https://www.cnblogs.com/renhui/p/9223969.html
    //ffmpeg.exe -y -i d:/1.aac -i d:/1.mp4 -map 0:0 -map 1:0 d:/out.mp4
    //-y引數表示預設yes覆蓋檔案
    //不同平臺可執行檔案路徑改成自己的
    QString binFile = qApp->applicationDirPath() + "/ffmpeg.exe";

    //下面兩種方法都可以,怎麼方便怎麼來
#if 0
    QString cmd = QString("%1 -y -i %2 -i %3 -map 0:0 -map 1:0 %4").arg(binFile).arg(h264File).arg(aacFile).arg(mp4File);
    start(cmd);
#else
    QStringList args;
    args << "-y";
    args << "-i" << h264File;

    //如果存在音訊檔案則新增
    if (QFile(aacFile).exists()) {
        args << "-i" << aacFile;
    }

    //args << "-map" << "0:0";
    //args << "-map" << "1:0";
    args << mp4File;
    start(binFile, args);
#endif
}