亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

FFmpeg的C++封裝:FFmpegWrapper

系統(tǒng) 3299 0

下面介紹的API已過時,請下載最新版本的源代碼,并參考其注釋。新版本主要由 John 編寫,在舊版本的基礎(chǔ)上做了很多改進。

什么是FFmpeg?

FFmpeg 是一套完整的錄制、轉(zhuǎn)換、流化音視頻的解決方案,也是一個在 LGPL協(xié)議 下的開源項目。它包含了業(yè)界領(lǐng)先的音視頻編解碼庫。FFmpeg是在Linux操作系統(tǒng)下開發(fā)的,但它也能在其他操作系統(tǒng)下編譯,包括Windows。

整個項目由以下幾個部分組成:

  • ffmpeg:一個用來轉(zhuǎn)換視頻文件格式的命令行工具,它也支持從電視卡中實時的抓取和編碼視頻。
  • ffserver:一個基于HTTP協(xié)議(基于RTSP的版本正在開發(fā)中)用于實時廣播的多媒體服務器,它也支持實時廣播的時間平移。
  • ffplay:一個用 SDL 和FFmpeg庫開發(fā)的簡單的媒體播放器。
  • libavcodec:一個包含了所有FFmpeg音視頻編解碼器的庫。為了保證最優(yōu)的性能和高可復用性,大多數(shù)編解碼器都是從頭開發(fā)的。
  • libavformat:一個包含了所有的普通音視頻格式的解析器和產(chǎn)生器的庫。

FFmpegWrapper僅使用了libavcodec和libavformat這兩部分。

什么是FFmpegWrapper?

FFmpegWrapper:

  • 是一個在Windows下用VS2005編寫的C++ Win32動態(tài)庫。
  • 用面向?qū)ο蟮姆椒ǚ庋b了FFmpeg庫中常用的API,使之易于使用,不需要開發(fā)人員了解很多音視頻編解碼的知識。
  • 其中99%的代碼符合C++標準,很容易移植到其他平臺。
  • farthinker 開發(fā)和維護。

?

為什么要使用FFmpegWrapper?

對于一個對視頻編解碼不太了解的開發(fā)者來說,用FFmpeg的庫來編寫應用絕對是一件痛苦的事情。首先需要編譯從SVN下載的源代碼(FFmpeg官方只提供源代碼……)。如果是 在Windows下編譯 ,麻煩就開始了(當然你也可以直接使用別人編譯好的 SDK , 跳過這一步)。當你好不容易編譯好一個可以使用的動態(tài)庫之后,你會發(fā)現(xiàn)很難找到合適的文檔來學習如何使用FFmpeg的庫,你只能一邊參考示例代碼一邊摸 索使用方法。然后你會發(fā)現(xiàn)問題一個接一個的出現(xiàn),你又不知從何處下手來解決。總之,使用FFmpeg的學習成本是很高的。

FFmpegWrapper的目的就在于讓FFmpeg的調(diào)用過程簡單化、面向?qū)ο蠡档褪褂肍Fmpeg的學習成本,讓對視頻編解碼不太了解的 開發(fā)人員也能輕松的使用FFmpeg。但是,簡化使用的同時也在一定程度上簡化了功能,F(xiàn)FmpegWrapper很難繼承FFmpeg庫的所有功能。所 以FFmpegWrapper適合一些編解碼需求相對簡單的應用,而不適合那些需求復雜靈活、擴展性很強的應用。

如何使用FFmpegWrapper來編解碼音視頻?

準備工作

首先下載FFmpegWrapper的庫文件(若是在非Windows平臺下使用,則需要下載源代碼自己編譯),然后將FFmpegWrapper 部署到項目中。部署的過程中需要注意的是,最好不要改變ffmpeg文件夾相對于FFmpegWrapper.h的路徑,若必須要改變,組需要修改 FFmpegWrapper.h中#include “ffmpeg/include/avformat.h”的路徑。調(diào)用動態(tài)庫的具體方法這里就不贅述了。

使用FFmpegWrapper編碼音視頻

指定音視頻參數(shù)

首先需要指定一些音視頻的參數(shù)。FFmpegWrapper中用FFmpegVideoParam和FFmpegAudioParam這兩個類來表示音視頻的參數(shù)。下面的例子指定了一個flv視頻的參數(shù):

  1. //指定視頻參數(shù),從左到右分別是:寬、高、像素格式、比特率、幀率
  2. FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
  3. //指定視頻參數(shù),從左到右分別是:比特率、采樣率、聲道數(shù)
  4. FFmpegAudioParam audioParam(64000, 44100, 2);
    //指定視頻參數(shù),從左到右分別是:寬、高、像素格式、比特率、幀率

FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);

//指定視頻參數(shù),從左到右分別是:比特率、采樣率、聲道數(shù)

FFmpegAudioParam audioParam(64000, 44100, 2);
  

若視頻中沒有視頻流(音頻流),則可以初始化一個空的FFmpegVideoParam(FFmpegAudioParam):

  1. FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
  2. //沒有音頻流,初始化一個空的音頻參數(shù)對象
  3. FFmpegAudioParam audioParam();
    FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);

//沒有音頻流,初始化一個空的音頻參數(shù)對象

FFmpegAudioParam audioParam();
  

初始化FFmpegEncoder對象

用音視頻參數(shù)初始化FFmpegEncoder對象:

  1. FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
  2. FFmpegAudioParam audioParam(64000, 44100, 2);
  3. //參數(shù)從左到右分別是:FFmpegVideoParam、FFmpegAudioParam 、編碼輸出文件名
  4. FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv" );
    FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);

FFmpegAudioParam audioParam(64000, 44100, 2);

//參數(shù)從左到右分別是:FFmpegVideoParam、FFmpegAudioParam 、編碼輸出文件名

FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv");
  

其中第三個參數(shù)包含了輸出文件的路徑、名字和后綴,并且是可選的參數(shù),也就是說可以沒有輸出文件。但是在沒有輸出文件的時候需要音視頻參數(shù)中指定codec的名稱,因為FFmpegEncoder不能從文件后綴判斷出使用什么codec:

  1. FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25, "flv" );
  2. FFmpegAudioParam audioParam(64000, 44100, 2, "libmp3lame" );
  3. //參數(shù)從左到右分別是:FFmpegVideoParam、FFmpegAudioParam 、編碼輸出文件名
  4. FFmpegEncoder testEncoder(videoParam, audioParam);
    FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25, "flv");

FFmpegAudioParam audioParam(64000, 44100, 2, "libmp3lame");

//參數(shù)從左到右分別是:FFmpegVideoParam、FFmpegAudioParam 、編碼輸出文件名

FFmpegEncoder testEncoder(videoParam, audioParam);
  

逐幀編碼音視頻

開始編碼之前還需要先調(diào)用FFmpegEncoder對象的open方法,打開相應的codec和輸出文件:

  1. FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);
  2. FFmpegAudioParam audioParam(64000, 44100, 2);
  3. FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv" );
  4. ?
  5. testEncoder.open();
    FFmpegVideoParam videoParam(352, 288, PIX_FMT_YUV420P, 400000, 25);

FFmpegAudioParam audioParam(64000, 44100, 2);

FFmpegEncoder testEncoder(videoParam, audioParam, "test.flv");



testEncoder.open();
  

然后就可以調(diào)用FFmpegEncoder對象的writeVideoFrame(writeAudioFrame)方法來逐幀的編碼并輸出視(音)頻了:

  1. //其中videoFrameData是uint8_t *(unsigned char *)類型的參數(shù)
  2. testEncoder.writeVideoFrame(videoFrameData);
  3. ?
  4. //其中audioFrameData是short *類型的參數(shù)
  5. testEncoder.writeAudioFrame(audioFrameData);
    //其中videoFrameData是uint8_t *(unsigned char *)類型的參數(shù)

testEncoder.writeVideoFrame(videoFrameData);



//其中audioFrameData是short *類型的參數(shù)

testEncoder.writeAudioFrame(audioFrameData);
  

如果編碼之后不需要輸出,即沒有輸出文件,則使用encodeVideoFrame和getVideoBuffer(encodeAudioFrame和getAudioBuffer)方法來編碼和獲得編碼后的數(shù)據(jù):

  1. //其中videoFrameData是uint8_t *(unsigned char *)類型的參數(shù)
  2. testEncoder.encodeVideoFrame(videoFrameData);
  3. uint8_t *encodedVideo = testEncoder.getVideoBuffer();
  4. ?
  5. //其中audioFrameData是short *類型的參數(shù)
  6. testEncoder.encodeAudioFrame(audioFrameData);
  7. uint8_t *encodedAudio = testEncoder.getAudioBuffer();
    //其中videoFrameData是uint8_t *(unsigned char *)類型的參數(shù)

testEncoder.encodeVideoFrame(videoFrameData);

uint8_t *encodedVideo = testEncoder.getVideoBuffer();



//其中audioFrameData是short *類型的參數(shù)

testEncoder.encodeAudioFrame(audioFrameData);

uint8_t *encodedAudio = testEncoder.getAudioBuffer();
  

編碼的過程中,還可以獲得音視頻的時間戳(pts)來處理音視頻同步(暫不適用于沒有輸出文件的情況),下面是一個例子:

  1. short *audioData;
  2. uint8_t *videoData;
  3. double videoPts, audioPts;
  4. ?
  5. videoPts = testEncoder.getVideoPts();
  6. audioPts = testEncoder.getAudioPts();
  7. ?
  8. /* output 5 seconds test video file */
  9. while (audioPts < 5) {
  10. ???? if (audioPts <= videoPts) {
  11. ???????? audioData = getTestAudioData();
  12. ???????? testEncoder.writeAudioFrame(audioData);
  13. ???? } else {
  14. ???????? videoData = getVideoFrame();
  15. ???????? testEncoder.writeVideoFrame(videoData);
  16. ???? }
  17. ???? audioPts = testEncoder.getAudioPts();
  18. ???? videoPts = testEncoder.getVideoPts();
  19. }
    short *audioData;

uint8_t *videoData;

double videoPts, audioPts;



videoPts = testEncoder.getVideoPts();

audioPts = testEncoder.getAudioPts();



/* output 5 seconds test video file */

while (audioPts < 5) {

 if (audioPts <= videoPts) {

  audioData = getTestAudioData();

  testEncoder.writeAudioFrame(audioData);

 } else {

  videoData = getVideoFrame();

  testEncoder.writeVideoFrame(videoData);

 }

 audioPts = testEncoder.getAudioPts();

 videoPts = testEncoder.getVideoPts();

}
  

完成編碼后還需要調(diào)用FFmpegEncoder對象的close方法,關(guān)閉codec和輸出文件并釋放資源:

    testEncoder.close();
  

使用FFmpegWrapper解碼音視頻

初始化FFmpegDecoder對象

首先初始化一個FFmpegDecoder對象,并傳入輸入文件的名稱(包括路徑、名字和后綴):

  1. FFmpegDecoder testDecoder( "test.flv" );
    FFmpegDecoder testDecoder("test.flv");
  

逐幀解碼音視頻

開始解碼之前還需要先調(diào)用FFmpegDecoder對象的open方法,打開相應的codec和輸入文件:

  1. FFmpegDecoder testDecoder( "test.flv" );
  2. ?
  3. testDecoder.open();
    FFmpegDecoder testDecoder("test.flv");



testDecoder.open();
  

然后就可以調(diào)用FFmpegDecoder對象的decodeFrame方法來逐幀的解碼音視頻文件了:

  1. //decodeFrame的返回值表示當前解碼的幀的狀態(tài):
  2. //?? 0 - 視頻幀
  3. //?? 1 - 音頻幀
  4. // -1 - 文件末尾或解碼出錯
  5. int signal = testDecoder.decodeFrame()
    //decodeFrame的返回值表示當前解碼的幀的狀態(tài):

//  0 - 視頻幀

//  1 - 音頻幀

// -1 - 文件末尾或解碼出錯

int signal = testDecoder.decodeFrame()
  

解碼之后可以通過FFmpegDecoder對象的getVideoFrame(getAudioFrame)方法來獲得解碼后的視(音)頻數(shù)據(jù),下面是一個完整的例子:

  1. int signal;
  2. uint8_t *decodedVideo;
  3. short *decodedAudio;
  4. FFmpegDecoder testDecoder( "test.flv" );
  5. ffencoder.open();
  6. ?
  7. while ( true ) {
  8. ???? signal = testDecoder.decodeFrame();
  9. ?
  10. ???? if (signal == 0) {
  11. ???????? decodedVideo = ffdecoder.getVideoFrame();
  12. ???????? //處理解碼之后的視頻數(shù)據(jù)
  13. ???? } else if (signal == 1) {
  14. ???????? decodedAudio = ffdecoder.getAudioFrame();
  15. ???????? //處理解碼之后的音頻數(shù)據(jù)
  16. ???? } else if (signal == -1) {
  17. ???????? break ;
  18. ???? }
  19. }
    int signal;

uint8_t *decodedVideo;

short *decodedAudio;

FFmpegDecoder testDecoder("test.flv");

ffencoder.open();



while (true) {

 signal = testDecoder.decodeFrame();



 if (signal == 0) {

  decodedVideo = ffdecoder.getVideoFrame();

  //處理解碼之后的視頻數(shù)據(jù)

 } else if (signal == 1) {

  decodedAudio = ffdecoder.getAudioFrame();

  //處理解碼之后的音頻數(shù)據(jù)

 } else if (signal == -1) {

  break;

 }

}
  

完成編碼后還需要調(diào)用FFmpegDecoder對象的close方法,關(guān)閉codec和輸入文件并釋放資源:

    testDecoder.close();
  

注意

  • FFmpegWrapper暫時沒有完整的轉(zhuǎn)碼功能,如有需要請使用FFmpeg提供的格式轉(zhuǎn)換工具ffmpeg.exe。
  • 上面的介紹只涉及到一部分FFmpegWrapper的公共API,詳細的API介紹和其他細節(jié)見FFmpegWrapper API參考(upcoming)。
  • farthinker只是一個web開發(fā)者,對音視頻的了解實在有限,所以FFmpegWrapper肯定存在一些潛在的問題,歡迎大家積極批評指正。

下載FFmpegWrapper

FFmpegWrapper的未來

就像上面提到的那樣,我只是一個web開發(fā)者,不是音視頻方面的專業(yè)人員。因為項目中有一些簡單的編碼需求,我才編寫了 FFmpegWrapper。我希望FFmpegWrapper能幫助像我這樣的音視頻的菜鳥,能讓剛接觸FFmpeg的朋友少走彎路。當然,我的能力和 精力實在有限,今后如果沒有更多的編解碼需求,我可能很難抽出大把時間繼續(xù)完善FFmepgWrapper。但我真心希望FFmpegWrapper能繼 續(xù)走下去,所以有心和我一起繼續(xù)編寫FFmpegWrapper朋友請和我聯(lián)系。

FFmpeg的C++封裝:FFmpegWrapper


更多文章、技術(shù)交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 色悠综合 | 国产成人精品第一区二区 | 久久视频免费观看 | 精品国产成人系列 | 日本另类αv欧美另类aⅴ | 婷婷亚洲综合 | 天天碰天天操 | 久久思re热9一区二区三区 | 久久久免费精品视频 | 久久精品国产福利 | 精品乱人伦一区二区三区 | 国产精品99久久久久久宅男 | 狠狠综合欧美综合欧美色 | 欧美精品国产综合久久 | 国产区视频在线观看 | 69性影院在线观看国产精品87 | 一二三区在线观看 | 欧美日韩一级黄色片 | 国产综合亚洲精品一区 | 天天操天天添 | 美女一级毛片视频 | 午夜欧美日韩 | 欧美精品一区二区三区观 | 青青草国产97免久久费观看 | 加勒比精品久久一区二区三区 | 午夜激情免费 | 欧美色综合高清免费 | 思99re久久这里只有精品首页 | 草草免费视频 | 国产一区二区三区在线观看视频 | 亚洲欧美综合 | 国产欧美综合在线一区二区三区 | 欧美另类性视频在线看 | 国产色婷婷精品免费视频 | 夜夜干天天操 | 日本高清不卡网站免费 | 六月婷婷色 | 日日干天天爽 | 四虎影视永久免费观看 | 亚洲精品一区二区乱码在线观看 | 成人凹凸短视频在线观看 |