動態

詳情 返回 返回

ffmpeg使用入門 - 動態 詳情

1 ffmpeg安裝

1.1 安裝vcpkg

直接從github上下載Release版本,並進行安裝

https://github.com/microsoft/vcpkg

從GitHub克隆vcpkg存儲庫。存儲庫包含用於獲取vcpkg可執行文件的腳本,以及由vcpkg社區維護的特選開放源代碼庫的註冊表。要執行此操作,請運行:

git clone https://github.com/microsoft/vcpkg.git

vcpkg特選註冊表是一組數量超過2000個的開源庫。 這些庫已通過vcpkg的持續集成管道進行驗證,可以協同工作。雖然vcpkg存儲庫不包含這些庫的源代碼,但它保存方案和元數據,以便在系統中生成和安裝它們。

導航到vcpkg目錄並執行啓動腳本:

cd vcpkg && bootstrap-vcpkg.bat

啓動腳本執行先決條件檢查並下載vcpkg可執行文件。就這麼簡單,vcpkg已安裝並可供使用。

配置VCPKG_ROOT環境變量。

set "VCPKG_ROOT=C:\path\to\vcpkg"
set PATH=%VCPKG_ROOT%;%PATH%

以這種方式設置環境變量只會影響當前終端會話。若要使這些更改在所有會話中永久存在,請通過“Windows 系統環境變量”面板進行設置。

1.2 安裝其他庫

一切準備就緒,在cmd命令行執行如下命令安裝ffmpeg靜態庫:

vcpkg.exe install ffmpeg:x86-windows-static

在cmd命令行執行如下命令安裝ffmpeg動態庫:

vcpkg.exe install ffmpeg:x86-windows

安裝過程如下:

使用vcpkg list可以列出當前所有安裝的庫,使用vcpkg remove可以卸載安裝的庫。

1.3 exe文件獲取

vcpkg install ffmpeg默認不會安裝ffmpeg.exe等工具程序,它只安裝了作為庫使用的 FFmpeg C/C++開發接口,如:avcodec、avformat、avutil、swscale等.lib/.dll,以及include/*/*.h頭文件,因為vcpkg的目標是為C/C++項目提供開發依賴(SDK/庫),而不是提供命令行工具(像ffmpeg.exe這種完整可執行程序)。要想使用ffmpeg.exe這種命令行工具,可以從官網https://ffmpeg.org/download.html下載預編譯版本:

image

如下載 “release full”版本,解壓後會包含ffmpeg.exe、ffplay.exe、ffprobe.exe三個可執行文件,將其路徑添加到環境變量PATH中即可在cmd命令行直接使用這三個命令。

2 命令使用介紹

2.1 ffmpeg.exe

1. 查看幫助

-h,-?,-help,--help [arg]:顯示幫助。可以指定可選參數來打印有關特定項目的幫助,如果未指定參數 arg,則僅顯示基本工具選項,若指定參數,arg 可能值為:

image

long:除了基本工具選項之外,還打印高級工具選項。

full:輸出完整的選項列表,包括編碼器、解碼器、解複用器、複用器、過濾器等的共享和私有選項。

type=name:輸出相應類型的相關參數。如:decoder=msmpeg4v2,輸出有關名為msmpeg4v2編碼器的詳細信息。

Decoder msmpeg4v2 [MPEG-4 part 2 Microsoft variant version 2]:
    General capabilities: horizband dr1
    Threading capabilities: none

接下來還有以下幫助選項

-version:顯示查看版本(包括子模塊的詳細版本信息)。

-muxers:顯示所有支持的封裝格式(如mp4、mkv)

-demuxers:顯示所有支持的解封裝格式

-devices:顯示支持的音視頻輸入/輸出設備

-decoders:顯示所有可用的解碼器

-encoders:顯示所有可用的編碼器(如H.264、AAC等)

-filters:顯示可用過濾器。

-pix_fmts:顯示支持的像素格式(如yuv420p,rgb24)

-layouts:顯示標準的音頻通道佈局(如stereo,5.1)

-sample_fmts:顯示音頻採樣格式(如s16,flt,dbl)

更多詳細命令參數見官方文檔及後續部分介紹,如果希望將幫助文檔保存到文件中,可以輸入ffmpeg -h full > ffmpeg_h_full.log命令,將輸出結果重定向到一個文件中,然後再打開該文件即可查看完整的幫助文檔。

2. 命令執行常用參數

-y:若輸出目錄已存在同名同容器格式的文件,直接輸出當前文件將其覆蓋而不再詢問。

-n:不要覆蓋輸出文件,如果已經存在同名同容器格式的文件,立即結束運行,效果與 -y 相反。

-f:強制設定文件格式,需使用能力集列表中的名稱(缺省是根據擴展名選擇的)。

-hide_banner:隱藏FFmpeg版本號及一些描述信息。

-itsoffset:用於指定輸入文件的偏移時間,後接時間值,正值向後偏移,負值向前偏移,可以對齊多個輸入文件的時間軸,使它們在合併或處理時保持同步。

3. 視頻幀操作

(1)基本時間剪輯

-ss:設置媒體文件的起始時間,如想要從視頻的2秒開始剪切處理,我們輸入“-ss 2”,如想要更精確的時間也可以,如“-ss 1:23.789”表示設置從1分23秒789毫秒開始。

-t:設置媒體文件的持續時間,用法同上面-ss ,如“-t 0:13.234”表示持續13秒234毫秒。

-to:設置媒體文件的截止時間,用法與前兩個相同,-to和-t是互斥的,並且-t具有優先權,兩者不能同時出現使用。

ffmpeg.exe -ss 0:10 -t 0:5 -i .\zhouxingchi.mp4 -c copy out.mp4

如以上命令將mp4文件從10秒處開始截取並持續5秒,最後將該部分另存到out.mp4文件。

通常將-ss 、-t和-to放在-i之前,這些參數用於指定輸入文件的開始時間(-ss)、持續時間(-t)或結束時間(-to),因此它們需要在輸入文件(-i)之前進行設置,以確保正確地應用到指定的輸入文件上,以防混亂。

(2)截取視頻中某幀

ffmpeg -ss 00:00:10 -i zhouxingchi.mp4 -frames:v 1 -q:v 2 out.jpg

-ss 00:00:10:跳到第 10 秒,-frames:v 1:只保存一幀,-q:v 2:圖像質量(1 為最好,31 最差,建議用 2~4)。

(3)導出片段視頻幀

ffmpeg.exe -ss 13 -to 15 -i .\zhouxingchi.mp4 .\out\%03d.png

導出從視頻第13秒到15秒這兩秒內所有幀圖片。該命令還可以增加-vf "fps=1"參數,表示每秒截一幀:

ffmpeg.exe -ss 13 -to 20 -i .\zhouxingchi.mp4 -vf "fps=1" .\out\%03d.png

4. 錄屏操作

直接使用ffmpeg內置的gdigrab對桌面進行錄屏

ffmpeg -f gdigrab -i desktop -pix_fmt yuv420p D:\out.mp4

這個命令使用FFmpeg工具來捕獲Windows桌面並將其保存為一個mp4格式的視頻文件。“-f gdigrab”表示使用 GDI (Graphics Device Interface) 來捕獲屏幕;“-i desktop”指定要捕獲的對象為desktop即桌面內容;“-pix_fmt yuv420p”指定輸出視頻的像素格式為 YUV 4:2:0 planar。

5. 解封裝與解碼

通過ffmpeg命令,可以將MP4視頻轉為H.264流(裸流.264文件),然後再提取成YUV數據文件(原始像素數據)。

(1)將MP4轉為H.264裸流

ffmpeg -i out.mp4 -c:v libx264 -bsf:v h264_mp4toannexb -an -f h264 out.264

-c:v libx264:使用H.264編碼器(libx264)對視頻重新編碼。如果不想重新編碼,可以用-c:v copy保留原始流。
-bsf:v h264_mp4toannexb:將MP4封裝格式中的H.264轉為 Annex B 格式(常見裸流格式)。
-an:去除音頻流,僅保留視頻。
-f h264:指定輸出文件格式為H.264裸流。

(2)將H.264裸流轉為YUV數據

接着H.264裸流中的編碼數據需要解碼後才能獲得原始YUV數據。

ffmpeg -i out.264 -pix_fmt yuv420p -vsync 0 out.yuv

 -i output.264:輸入文件為 H.264 裸流。

-pix_fmt yuv420p:指定輸出像素格式為 YUV 4:2:0(標準格式)。

-vsync 0:禁用幀同步,確保輸出的幀數與輸入一致。

output.yuv:輸出文件名。

(3)直接從MP4提取YUV

如果不需要中間的 H.264 裸流步驟,可以直接從 MP4 轉為 YUV:

ffmpeg -i out.mp4 -pix_fmt yuv420p -fps_mode passthrough out.yuv

6. 碼流提取

(1)提取視頻

通過移除音頻流,可以單獨提取視頻使用,保留原始的視頻編碼。具體命令如下:

ffmpeg -i out.mp4 -vcodec copy -an video0.mp4

(2)提取音頻

利用ffmpeg命令,可輕鬆實現視頻中的音頻提取。具體命令如下:

ffmpeg -i out.mp4 -acodec copy -vn voice.aac

如果需要將AAC音頻流直接複製到MP3容器中,則需要執行如下命令:

7. 添加LOGO

使用ffmpeg命令將LOGO(例如logo.png)疊加到原始視頻文件(例如out.mp4)上,可以通過調整overlay參數的位置來改變LOGO的放置位置。具體命令如下:

左上角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay out1.mp4
右上角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=W-w out2.mp4
左下角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=0:H-h out3.mp4
右下角:ffmpeg -i out.mp4 -i logo.png -filter_complex overlay=W-w:H-h out4.mp4

以上命令中W是視頻分辨率中的寬,w是logo文件圖片的寬,H是視頻分辨率中的高,h是logo文件圖片的高。

2.2 ffplay.exe

ffplay是基於SDL與ffmpeg庫實現的一個輕量級媒體播放器,可以使用它來播放原始的YUV/PCM 數據、編碼後的H.264/H.265等數據,封裝好的MP4/M4A等數據,還可以播放來自網絡的流媒體數據。

1. 命令行格式

ffplay [選項] [輸入文件路徑]。它使用ffmpeg庫進行解碼和解碼,並且可以通過命令行參數來控制播放行為,如調整音量、播放速度、畫面比例等。

2. 常用基本選項

-x <寬度> 和 -y <高度>:強制設置視頻顯示的寬度和高度。

-fs:以全屏模式啓動。

-an、-vn、-sn:分別禁用音頻、視頻和字幕。

-acodec、-vcodec、-scodec:分別強制使用設置的音頻、視頻和字幕解碼器來播放。

-threads <個數>:設置線程個數,可以控制 ffplay 在解碼和渲染過程中的並行度,從而提高播放性能。

-ss <開始時間>:從特定的時間點開始播放。

-t <持續時間>:設置播放的持續時間。

-vol <音量>:設置播放的初始音量。

-vf 和 -af:應用視頻和音頻濾鏡。

-autoexit:視頻播放完畢後自動退出。

-loop <循環播放次數>:指定文件循環播放次數。

-showmode [mode]:設置顯示模式,mode 默認為0顯示視頻,為1顯示音頻波形,為2顯示音頻頻譜。

3. 播放中按鍵控制

w:切換播放模式,比如在音頻波形圖、音頻頻譜圖、視頻畫面之間切換。

s:步進模式,每按一次就播放下一幀圖像。

right:快進 10s。

left:快退 10s。

up:快進 1min。

down:快退 1min。

space:暫停。

esc/q:退出播放。

2.3 ffprobe.exe

ffprobe是FFmpeg源碼編譯後生成的一個可執行程序,可以從媒體文件或網絡媒體流中獲得音視頻及媒體容器的參數信息,用於查看和分析多媒體文件。

1. 命令行格式

ffprobe [選項] [ [-i] 輸入文件路徑],不加任何選項時輸出如下所示:

image

根據ffprobe的輸出,視頻文件"zhouxingchi.mp4"包含了兩個流:一個是視頻流,另一個是音頻流。

  • 視頻流的信息:編解碼器 av1 (libdav1d) (Main) (av01 / 0x31307661)、像素格式 yuv420p(tv, bt709)、分辨率 1920x1080、幀率 24 fps、比特率 845 kb/s。
  • 音頻流的信息:編解碼器 aac (LC) (mp4a / 0x6134706D)、採樣率 44100 Hz、stereo(立體聲)、比特率 128 kb/s。

2. 常用基本選項

-show_format:查看媒體文件的封裝信息,輸出內容的前半部分和不加選項時的輸出一樣,後半部分會得到該視頻文件的封裝文件信息如下所示。

image

-show_streams:查看媒體文件的流信息,如下所示其中一條媒體流的信息:

[STREAM]
index=0
codec_name=av1
codec_long_name=Alliance for Open Media AV1
profile=Main
codec_type=video
codec_tag_string=av01
codec_tag=0x31307661
width=1920
height=1080
coded_width=1920
coded_height=1080
closed_captions=0
film_grain=0
has_b_frames=0
sample_aspect_ratio=1:1
display_aspect_ratio=16:9
pix_fmt=yuv420p
level=8
color_range=tv
color_space=bt709
color_transfer=bt709
color_primaries=bt709
chroma_location=unspecified
field_order=unknown
refs=1
id=0x1
r_frame_rate=24/1
show_streams

-show_packets:查看文件的所有數據包信息,一個視頻文件由多個數據包組成。

-show_frames:查看媒體文件的每一幀信息,我們分析其中兩個如下所示。如下是音頻幀類型,然後key_frame=1表示這是IDR frame,如果key_frame=0表示這是Non-IDR frame。

-select_streams <type>:選擇特定類型的流進行顯示,<type>可以是v(視頻)、a(音頻)或s(字幕)。

-of/-print_format <format>:指定輸出格式,常用的輸出格式有csv、json、flat、xml等。

ffprobe.exe -i .\out.mp4 -show_format -show_streams -of json

3 SDK使用Demo實例

3.1 SDL編譯

SDL(Simple DirectMedia Layer)是一套開放源代碼的跨平台多媒體開發庫,使用C語言寫成。SDL提供了數種控制圖像、聲音、輸出入的函數,讓開發者只要用相同或是相似的代碼就可以開發出跨多個平台(Linux、Windows、Mac OS X等)的應用軟件。該庫編譯比較簡單,直接下載源碼使用CMake進行配置編譯即可,需要注意的是要確定編譯版本是32位還是64位版本,這裏編譯器用的VS2019,編譯的是64位版本。

image

配置完成後,直接用VS打開生成的工程進行編譯即可。

3.2 基於SDL的視頻播放demo

可以直接在SDL解決方案下添加Console項目,主文件內容如下:

  1 extern "C" {
  2 #include <libavcodec/avcodec.h>
  3 #include <libavformat/avformat.h>
  4 #include <libavutil/imgutils.h>
  5 #include <libswscale/swscale.h>
  6 }
  7 
  8 #define SDL_MAIN_HANDLED
  9 #include <SDL2/SDL.h>
 10 #include <iostream>
 11 
 12 int main(int argc, char *argv[])
 13 {
 14     if (argc < 2) {
 15         std::cerr << "請指定視頻文件路徑,例如:\n";
 16         std::cerr << "  " << argv[0] << " sample.mp4\n";
 17         return -1;
 18     }
 19 
 20     const char *filepath = argv[1];
 21 
 22     avformat_network_init();
 23 
 24     AVFormatContext *fmt_ctx = nullptr;
 25     if (avformat_open_input(&fmt_ctx, filepath, nullptr, nullptr) < 0) {
 26         std::cerr << "無法打開文件: " << filepath << "\n";
 27         return -1;
 28     }
 29 
 30     if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
 31         std::cerr << "無法獲取視頻流信息\n";
 32         return -1;
 33     }
 34 
 35     int video_stream_idx = -1;
 36     for (unsigned int i = 0; i < fmt_ctx->nb_streams; ++i) {
 37         if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
 38             video_stream_idx = i;
 39             break;
 40         }
 41     }
 42 
 43     if (video_stream_idx == -1) {
 44         std::cerr << "沒有找到視頻流\n";
 45         return -1;
 46     }
 47 
 48     AVCodecParameters *codecpar = fmt_ctx->streams[video_stream_idx]->codecpar;
 49     const AVCodec *decoder = avcodec_find_decoder(codecpar->codec_id);
 50     if (!decoder) {
 51         std::cerr << "找不到解碼器\n";
 52         return -1;
 53     }
 54 
 55     AVCodecContext *codec_ctx = avcodec_alloc_context3(decoder);
 56     avcodec_parameters_to_context(codec_ctx, codecpar);
 57     avcodec_open2(codec_ctx, decoder, nullptr);
 58 
 59     AVFrame *frame = av_frame_alloc();
 60     AVFrame *rgb_frame = av_frame_alloc();
 61     AVPacket *pkt = av_packet_alloc();
 62 
 63     int width = codec_ctx->width;
 64     int height = codec_ctx->height;
 65     enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P;
 66 
 67     struct SwsContext *sws_ctx = sws_getContext(
 68         width, height, codec_ctx->pix_fmt,
 69         width, height, dst_pix_fmt,
 70         SWS_BILINEAR, nullptr, nullptr, nullptr);
 71 
 72     int num_bytes = av_image_get_buffer_size(dst_pix_fmt, width, height, 1);
 73     uint8_t *buffer = (uint8_t *)av_malloc(num_bytes * sizeof(uint8_t));
 74     av_image_fill_arrays(rgb_frame->data, rgb_frame->linesize, buffer, dst_pix_fmt, width, height, 1);
 75 
 76     // 初始化 SDL
 77     SDL_Init(SDL_INIT_VIDEO);
 78     SDL_Window *window = SDL_CreateWindow("FFmpeg Player",
 79                                           SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
 80                                           width, height, SDL_WINDOW_SHOWN);
 81     SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);
 82     SDL_Texture *texture = SDL_CreateTexture(renderer,
 83                                              SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, width, height);
 84 
 85     // 解碼 & 渲染循環
 86     while (av_read_frame(fmt_ctx, pkt) >= 0) {
 87         if (pkt->stream_index == video_stream_idx) {
 88             if (avcodec_send_packet(codec_ctx, pkt) == 0) {
 89                 while (avcodec_receive_frame(codec_ctx, frame) == 0) {
 90                     sws_scale(sws_ctx,
 91                               frame->data, frame->linesize,
 92                               0, height,
 93                               rgb_frame->data, rgb_frame->linesize);
 94 
 95                     SDL_UpdateYUVTexture(texture, nullptr,
 96                                          rgb_frame->data[0], rgb_frame->linesize[0],
 97                                          rgb_frame->data[1], rgb_frame->linesize[1],
 98                                          rgb_frame->data[2], rgb_frame->linesize[2]);
 99 
100                     SDL_RenderClear(renderer);
101                     SDL_RenderCopy(renderer, texture, nullptr, nullptr);
102                     SDL_RenderPresent(renderer);
103                     SDL_Delay(1000 / 30); // 簡單幀率控制
104                 }
105             }
106         }
107         av_packet_unref(pkt);
108 
109         SDL_Event event;
110         SDL_PollEvent(&event);
111         if (event.type == SDL_QUIT) {
112             break;
113         }
114     }
115 
116     // 清理
117     sws_freeContext(sws_ctx);
118     av_free(buffer);
119     av_frame_free(&frame);
120     av_frame_free(&rgb_frame);
121     av_packet_free(&pkt);
122     avcodec_free_context(&codec_ctx);
123     avformat_close_input(&fmt_ctx);
124 
125     SDL_DestroyTexture(texture);
126     SDL_DestroyRenderer(renderer);
127     SDL_DestroyWindow(window);
128     SDL_Quit();
129 
130     return 0;
131 }
main.cpp

注意第8行語句,如果不包含該語句編譯時會提示錯誤:

MSVCRT.lib(exe_main.obj) : error LNK2001: 無法解析的外部符號 _main

在網上查了下原因,因為SDL2在SDL_main.h中定義瞭如下代碼:

#if defined(SDL_MAIN_NEEDED) || defined(SDL_MAIN_AVAILABLE)
#define main    SDL_main
#endif

它會把main重命名為SDL_main,然後SDL庫中會試圖用它自己的WinMain入口去調用你的SDL_main,從而接管程序,這在Windows GUI項目中是正常的,但現在用的是控制枱項目(需要標準main函數),但是上面的語句已經將main重定向到SDL_main,所以鏈接時會提示“無法解析的外部符號 _main”。

頭文件包含目錄配置如下:

image

鏈接器配置如下:

image

輸入庫包含如下:

SDL2d.lib;avformat.lib;avcodec.lib;avutil.lib;swscale.lib;

為了能在VS環境下直接運行調試,可以在調試選項配置中增加運行庫目錄到PATH環境變量,並給出要播放的文件為命令參數。

image

 

參考:

https://zhuanlan.zhihu.com/p/684158932

https://blog.csdn.net/lsb2002/article/details/136568262

Add a new 評論

Some HTML is okay.