欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android FFmpeg音視頻解碼播放示例詳解

 更新時(shí)間:2022年10月13日 10:45:18   作者:初一十五不吃飯  
這篇文章主要為大家介紹了Android FFmpeg音視頻解碼播放示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

看到很多都對音視頻這塊非常的感興趣,接下來就長篇贅述一下音視頻的前前后后,應(yīng)該從明天開始從音視頻的初中高三個(gè)層次展開淺談??

本文分別為:

  • FFmpeg簡介
  • FFmpeg音視頻解碼播放
  • Clang編譯FFmpeg常見問題

今天來說一下關(guān)于今天先說一下FFmpeg一些內(nèi)容

內(nèi)容如下:

1.2022最新Android11位大廠面試專題,128道附答案

2.音視頻大合集,從初中高到面試應(yīng)有盡有

3.Android車載應(yīng)用大合集,從零開始一起學(xué)

4.性能優(yōu)化大合集,告別優(yōu)化煩惱

5.Framework大合集,從里到外分析的明明白白

6.Flutter大合集,進(jìn)階Flutter高級工程師

7.compose大合集,擁抱新技術(shù)

8.Jetpack大合集,全家桶一次吃個(gè)夠

9.架構(gòu)大合集,輕松應(yīng)對工作需求

10.Android基礎(chǔ)篇大合集,根基穩(wěn)固高樓平地起

整理不易,關(guān)注一下吧。開始進(jìn)入正題,?( ´???` ) ??

一丶FFmpeg簡介

1.簡介

FFmpeg(FastForward Mpeg)是一款遵循GPL的開源軟件,在音視頻處理方面表現(xiàn)十分優(yōu)秀,幾乎囊括了現(xiàn)存所有的視音頻格式的編碼,解碼、轉(zhuǎn)碼、混合、過濾及播放。同時(shí)也是一款跨平臺(tái)的軟件,完美兼容Linux、Windows、Mac OSX等平臺(tái)。其實(shí)它由3大部件組成:

FFmpeg:由命令行組成,用于多媒體格式轉(zhuǎn)換 FFplay:基于FFmpeg開源代碼庫libraries做的多媒體播放器 FFprobe:基于FFmpeg做的多媒體流分析器

源碼下載

2.FFmpeg兩個(gè)強(qiáng)大功能

1.命令功能

2.api功能

2.1 命令功能

應(yīng)用程序使用方法

上圖實(shí)例是將一個(gè)bus.avi中視頻分離出來,利用三個(gè)應(yīng)用程序中的ffmpeg.exe。

ffmpeg –y –i input –vcodeccopy –an output.avi

其中-y表示覆蓋同名文件,-i表示輸入文件即bus.avi,-vcodec表示編碼方式,后面的copy表示用原來的編碼方式,即不重新編碼,-an表示去除音頻,后面的busv.avi表示分離出的視頻文件。

同理將視頻中的音頻文件分離出來的命令行為:

ffmpeg -ibus.avi -acodec copy -vn busa.wav

上面舉例說明了應(yīng)用程序的用法,應(yīng)用程序的命令行相對代碼要簡單很多,也能實(shí)現(xiàn)例如音視頻分離、轉(zhuǎn)碼、播放等各種功能,如視頻轉(zhuǎn)碼的命令行為:

ffmpeg -y -i input.mp4 -vcodec libx264 -acodec copy output.mp4

這個(gè)命令用于剪切視頻,-ss表示從第幾秒開始,如上實(shí)例為從第5秒開始,-t代表剪持續(xù)幾秒長度的視頻,如上實(shí)例就是剪10秒長度的視頻,copy表示視頻編碼格式和音頻編碼格式與原視頻統(tǒng)一。

ffmpeg -ss 0:0:5 -t 0:0:10 -i input.avi -vcodec copy -acodec copy output.avi

分離視頻音頻流

ffmpeg -i input_file -vcodec copy -an output_file_video //分離視頻流
ffmpeg -i input_file -acodec copy -vn output_file_audio //分離音頻流

視頻解復(fù)用

ffmpeg –i test.mp4 –vcodec copy –an –f m4v test.264
ffmpeg –i test.avi –vcodec copy –an –f m4v test.264

視頻轉(zhuǎn)碼

ffmpeg –i test.mp4 –vcodec h264 –s 352*278 –an –f m4v test.264 //轉(zhuǎn)碼為碼流原始文件
ffmpeg –i test.mp4 –vcodec h264 –bf 0 –g 25 –s 352*278 –an –f m4v test.264 //轉(zhuǎn)碼為碼流原始文件
ffmpeg –i test.avi -vcodec mpeg4 –vtag xvid –qsame test_xvid.avi //轉(zhuǎn)碼為封裝文件

視頻封裝

ffmpeg –i video_file –i audio_file –vcodec copy –acodec copy output_file

視頻剪切

ffmpeg –i test.avi –r 1 –f image2 image-%3d.jpeg //提取圖片
ffmpeg -ss 0:1:30 -t 0:0:20 -i input.avi -vcodec copy -acodec copy output.avi
//剪切視頻
//-r 提取圖像的頻率,-ss 開始時(shí)間,-t 持續(xù)時(shí)間

視頻錄制

ffmpeg –i rtsp://192.168.3.205:5555/test –vcodec copy out.avi

YUV序列播放

ffplay -f rawvideo -video_size 1920x1080 input.yuv

YUV序列轉(zhuǎn)AVI

ffmpeg –s w*h –pix_fmt yuv420p –i input.yuv –vcodec mpeg4 output.avi

常用參數(shù)說明:

主要參數(shù):

  • -i 設(shè)定輸入流
  • -f 設(shè)定輸出格式
  • -ss 開始時(shí)間 視頻參數(shù):
  • -b 設(shè)定視頻流量,默認(rèn)為200Kbit/s
  • -r 設(shè)定幀速率,默認(rèn)為25
  • -s 設(shè)定畫面的寬與高 -aspect 設(shè)定畫面的比例
  • -vn 不處理視頻
  • -vcodec 設(shè)定視頻編解碼器,未設(shè)定時(shí)則使用與輸入流相同的編解碼器 音頻參數(shù):
  • -ar 設(shè)定采樣率 -ac 設(shè)定聲音的Channel數(shù)
  • -acodec 設(shè)定聲音編解碼器,未設(shè)定時(shí)則使用與輸入流相同的編解碼器 -an 不處理音頻

二丶FFmpeg音視頻解碼播放

前言

通常情況下,媒體文件以如MP4,MKV、FLV等等格式存在我們的計(jì)算機(jī),手機(jī)等設(shè)備中,而這些文件格式都屬于封裝格式,就是把音視頻數(shù)據(jù)按照相應(yīng)的規(guī)范,打包成文件。

1.FFmpeg 音視頻解碼流程

平常我們播放媒體文件時(shí),通常需要經(jīng)過以下幾個(gè)步驟

2.FFmpeg 音視頻解碼原理

2.1.解協(xié)議

將流媒體協(xié)議的數(shù)據(jù),解析為標(biāo)準(zhǔn)的相應(yīng)的封裝格式數(shù)據(jù)。視音頻在網(wǎng)絡(luò)上傳播的時(shí)候,常常采用各種流媒體協(xié)議,例如 HTTP,RTMP,或是 MMS 等等。這些協(xié)議在傳輸視音頻數(shù)據(jù)的同時(shí),也會(huì)傳輸一些信令數(shù)據(jù)。這些信令數(shù)據(jù)包括對播放的控制(播放,暫停,停止),或者對網(wǎng)絡(luò)狀態(tài)的描述等。

解協(xié)議的過程中會(huì)去除掉信令數(shù)據(jù)而只保留視音頻數(shù)據(jù)。例如,采用 RTMP 協(xié)議傳輸?shù)臄?shù)據(jù),經(jīng)過解協(xié)議操作后,輸出 FLV 格式的數(shù)據(jù)。

2.2.解封裝

將輸入的封裝格式的數(shù)據(jù),分離成為音頻流壓縮編碼數(shù)據(jù)和視頻流壓縮編碼數(shù)據(jù)。封裝格式種類很多,例如 MP4,MKV,RMVB,TS,F(xiàn)LV,AVI 等等,它的作用就是將已經(jīng)壓縮編碼的視頻數(shù)據(jù)和音頻數(shù)據(jù)按照一定的格式放到一起。例如,F(xiàn)LV 格式的數(shù)據(jù),經(jīng)過解封裝操作后,輸出 H.264 編碼的視頻碼流和AAC 編碼的音頻碼流。

2.3.解碼

將視頻/音頻壓縮編碼數(shù)據(jù),解碼成為非壓縮的視頻/音頻原始數(shù)據(jù)。解碼是整個(gè)系統(tǒng)中最重要也是最復(fù)雜的一個(gè)環(huán)節(jié)。通過解碼,壓縮編碼的視頻數(shù)據(jù)輸出成為非壓縮的顏色數(shù)據(jù),例如 YUV420P,RGB 等等;

2.4.音視頻同步

根據(jù)解封裝模塊處理過程中獲取到的參數(shù)信息,同步解碼出來的視頻和音頻數(shù)據(jù),并將視頻音頻數(shù)據(jù)送至系統(tǒng)的顯卡和聲卡播放出來。

2.5.FFmpeg音視頻解碼

通過前文,我們知道每一個(gè)媒體文件在被終端播放前主要經(jīng)過了兩個(gè)關(guān)鍵步驟,分別是解封裝和解碼。而在ffmpeg中,使用相關(guān)接口實(shí)現(xiàn)解封裝和解碼流程如下圖:

由上圖可知,我們需要重點(diǎn)關(guān)注下面這些FFmpeg的API接口:

  • av_register_all():注冊所有組件。
  • avformat_open_input():打開輸入視頻文件。
  • avformat_find_stream_info():獲取視頻文件信息。
  • avcodec_find_decoder():查找解碼器。
  • avcodec_open2():打開解碼器。
  • av_read_frame():從輸入文件讀取一幀壓縮數(shù)據(jù)。
  • avcodec_decode_video2():解碼一幀壓縮數(shù)據(jù)。

3.FFmpeg接口使用

在使用FFmpeg解碼媒體文件之前,首先需要注冊了容器和編解碼器有關(guān)的組件。

av_register_all()

如果我們需要播放網(wǎng)絡(luò)多媒體,則可以加載socket庫以及網(wǎng)絡(luò)加密協(xié)議相關(guān)的庫,為后續(xù)使用網(wǎng)絡(luò)相關(guān)提供支持。

avformat_network_init();

我們通過avformat_open_input()來打開一個(gè)媒體文件,并獲得媒體文件封裝格式的上下文

  //打開一個(gè)文件并解析??山馕龅膬?nèi)容包括:視頻流、音頻流、視頻流參數(shù)、音頻流參數(shù)、視頻幀索引
  int res = avformat_open_input(&pAVFormatCtx, url, NULL, NULL);
  LOGI("avformat_open_input %s %d", url, res);
  if(res != 0){
      LOGE("can not open url :%s", url);
      callJava->onCallError(CHILD_THREAD, 1001, "can not open url");
      exit = true;
      pthread_mutex_unlock(&init_mutex);
      return;
  }

通過avformat_find_stream_info()獲取媒體文件中,提取流的上下文信息,分離出音視頻流。

  //解碼時(shí),作用是從文件中提取流信,將所有的Stream的MetaData信息填充好,先read_packet一段數(shù)據(jù)解碼分析流數(shù)據(jù)
    if(avformat_find_stream_info(pAVFormatCtx, NULL) < 0){
       LOGE("can not find streams from %s", url);
       callJava->onCallError(CHILD_THREAD, 1002,"can not find streams from url");
       exit = true;
       pthread_mutex_unlock(&init_mutex);
       return;
  }

通過遍歷找出文件中的音頻流或視頻流

  for(int i = 0; i < pAVFormatCtx->nb_streams; i++){
          if(pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
             //得到音頻流
             if(audio == NULL){
                audio = new FFAudio(playstatus, pAVFormatCtx->streams[i]->codecpar->sample_rate, callJava);
                audio->streamIndex = i;
                audio->codecpar = pAVFormatCtx->streams[i]->codecpar;
                audio->duration = pAVFormatCtx->duration / AV_TIME_BASE;
                audio->time_base = pAVFormatCtx->streams[i]->time_base;
                duration = audio->duration;
                //av_q2d(time_base)=每個(gè)刻度是多少秒
                LOGI("audio stream_info[%d], duration:%d, time_base den:%d,sample_rate:%d",i, audio->duration, audio->time_base.den, pAVFormatCtx->streams[i]->codecpar->sample_rate);
                LOGI("audio stream_info[%d], duration %lld", i, pAVFormatCtx->duration);
             }
          } else if (pAVFormatCtx->streams[i]->codecpar->codec_type ==AVMEDIA_TYPE_VIDEO){
            //得到視頻流
            if (video == NULL){
                video = new FFVideo(playstatus, callJava);
                video->streamIndex = i;
                video->codecpar = pAVFormatCtx->streams[i]->codecpar;
                video->time_base = pAVFormatCtx>streams[i]->time_base;
                int num = pAVFormatCtx->streams[i]->avg_frame_rate.num;
                int den = pAVFormatCtx->streams[i]->avg_frame_rate.den;
                LOGI("video stream_info[%d], frame_rate num %d,den %d", i, num,den);
                if(num != 0 && den != 0){
                    int fps = num / den;//[25 / 1]
                    video->defaultDelayTime = 1.0 / fps;
                }
                LOGI("video stream_info[%d], defaultDelayTime is %f", i, video->defaultDelayTime);
           }
      }
  }

分離出音視頻流之后,可以找到對應(yīng)AVCodecContext,即編解碼器的上下文,用來尋找對應(yīng)的解碼器并設(shè)置。

  //查找對應(yīng)的解碼器 存儲(chǔ)編解碼器信息的結(jié)構(gòu)體
     AVCodec *avCodec = avcodec_find_decoder(codecpar->codec_id);// 軟解
     //avCodec = avcodec_find_decoder_by_name("mp3_mediacodec"); // 硬解
     if (!avCodec){
         LOGE("MFFmpeg::getCodecContext can not find decoder!");
         callJava->onCallError(CHILD_THREAD, 1003, "can not find decoder");
         exit = true;
         pthread_mutex_unlock(&init_mutex);
         return -1;
     }
     LOGI("getCodecContext codecpar-> 解碼類型:%d 編碼格式:%s" , codecpar->codec_type, avCodec->name);
     //配置解碼器
     *avCodecContext = avcodec_alloc_context3(avCodec);
     if (!*avCodecContext){
         LOGE("can not alloc new decodecctx");
         callJava->onCallError(CHILD_THREAD, 1004, "can not alloc new decodecctx");
         exit = true;
         pthread_mutex_unlock(&init_mutex);
         return -1;
  }

通過avcodec_open2()打開解碼器,解碼媒體文件。

   //打開編解碼器
 if(avcodec_open2(*avCodecContext, avCodec, 0) != 0){
   LOGE("cant not open strames");
   callJava->onCallError(CHILD_THREAD, 1006, "cant not open strames");
   exit = true;
   pthread_mutex_unlock(&init_mutex);
   return -1;
 }

所以第2,3,4,5四個(gè)步驟使用的關(guān)系如下圖

打開解碼器之后,通過av_read_frame()一幀一幀讀取壓縮數(shù)據(jù)。

  AVPacket \*avPacket = av\_packet\_alloc();
    //讀取具體的音/視頻幀數(shù)據(jù)
    int ret = av_read_frame(pAVFormatCtx, avPacket);
    if (ret==0){
        //stream_index:標(biāo)識(shí)該AVPacket所屬的視頻/音頻流
        if(avPacket->stream_index == audio->streamIndex){
           //LOGI("audio 解碼第 %d 幀 DTS:%lld PTS:%lld", count, avPacket->dts,avPacket->pts);
          audio->queue->putAVpacket(avPacket);
        } else if(avPacket->stream_index == video->streamIndex){
          //LOGI("video 解碼第 %d 幀 DTS:%lld PTS:%lld", count, avPacket->dts,avPacket->pts);
          count++;
          video->queue->putAVpacket(avPacket);
        } else{
          av_packet_free(&avPacket);
          av_free(avPacket);
          avPacket = NULL;
        }
  }

通過avcodec_decode_video2()/avcodec_decode_audio4解碼一幀視頻或者音壓縮數(shù)據(jù),通過AVPacket->AVFrame得到視頻像素?cái)?shù)據(jù)。

  //解碼AVPacket->AVFrame
  ret = avcodec_decode_audio4(pCodeCtx, frame, &got_frame, packet);
  //解碼一幀視頻壓縮數(shù)據(jù),得到視頻像素?cái)?shù)據(jù)
  ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);

三丶Clang編譯FFmpeg常見問題

1、命令找不到

錯(cuò)誤信息:

./build_android.sh: line 18: --enable-shared: command not found
./build_android.sh: line 20: --disable-static: command not found
./build_android.sh: line 22: --disable-doc: command not found
./build_android.sh: line 24: --disable-ffmpeg: command not found
./build_android.sh: line 26: --disable-ffplay: command not found
./build_android.sh: line 28: --disable-ffprobe: command not found
./build_android.sh: line 30: --disable-ffserver: command not found
./build_android.sh: line 32: --disable-avdevice: command not found

解決: 如果是直接copy網(wǎng)上的shell腳本,可能會(huì)是dos格式,請使用dos2unix build_android.sh 轉(zhuǎn)換一下,刪掉多余空格(這一點(diǎn)非常重要dos2unix 是一個(gè)工具,如果沒有安裝的話請先安裝一下:brew install dos2unix ,很快就完事。

2.xmakefile 文件沒有生成

錯(cuò)誤信息:

./android_config.sh: line 36: --enable-shared: command not found
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:91: /libavutil/Makefile: No such file or directory
Makefile:91: /ffbuild/library.mak: No such file or directory
Makefile:93: /fftools/Makefile: No such file or directory
Makefile:94: /doc/Makefile: No such file or directory
Makefile:95: /doc/examples/Makefile: No such file or directory
Makefile:160: /tests/Makefile: No such file or directory
make: *** No rule to make target `/tests/Makefile'. Stop.
Makefile:2: ffbuild/config.mak: No such file or directory
Makefile:40: /tools/Makefile: No such file or directory
Makefile:41: /ffbuild/common.mak: No such file or directory
Makefile:91: /libavutil/Makefile: No such file or directory
Makefile:91: /ffbuild/library.mak: No such file or directory
Makefile:93: /fftools/Makefile: No such file or directory
Makefile:94: /doc/Makefile: No such file or directory
Makefile:95: /doc/examples/Makefile: No such file or directory
Makefile:160: /tests/Makefile: No such file or directory

解決: 執(zhí)行 ./configure --disable-x86asm 生成config.mak文件

3.arm-linxu-androideabi-gcc is unable to

create an executable file

錯(cuò)誤信息:

/Users/aria/dev/android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-
4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc is unable to create an
executable file.

原因: 檢查ndk版本,android官方從 r18b 開始,已經(jīng)移除了gcc這個(gè)編譯工具詳情見ndk r18b修訂內(nèi)容 解決: 使用clang進(jìn)行編譯

4./android_config.sh: line 32: xxxxx No such file or directory

原因: .configure 后面的命令不能有注釋

解決: 刪除注釋的哪一行代碼

5、static declaration of 'xxx' follows non-static declaration

解決: config.h 搜索 lrint、lrintf、round、roundf 等對于的字符,將0修改為1

#define HAVE_LLRINT 1
#define HAVE_LLRINTF 1
#define HAVE_LRINT 1
#define HAVE_LRINTF 1
#define HAVE_ROUND 1
#define HAVE_ROUNDF 1
#define HAVE_CBRT 1
#define HAVE_CBRTF 1
#define HAVE_COPYSIGN 1
#define HAVE_TRUNC 1
#define HAVE_TRUNCF 1
#define HAVE_RINT 1
#define HAVE_HYPOT 1
#define HAVE_ERF 1

或直接使用 sed 來修改 config.h 文件

sed -i -e 's/#define HAVE_LLRINT 0/#define HAVE_LLRINT 1/g' config.h
sed -i -e 's/#define HAVE_LLRINTF 0/#define HAVE_LLRINTF 1/g' config.h
sed -i -e 's/#define HAVE_LRINT 0/#define HAVE_LRINT 1/g' config.h
sed -i -e 's/#define HAVE_LRINTF 0/#define HAVE_LRINTF 1/g' config.h
sed -i -e 's/#define HAVE_ROUND 0/#define HAVE_ROUND 1/g' config.h
sed -i -e 's/#define HAVE_ROUNDF 0/#define HAVE_ROUNDF 1/g' config.h
sed -i -e 's/#define HAVE_CBRT 0/#define HAVE_CBRT 1/g' config.h
sed -i -e 's/#define HAVE_CBRTF 0/#define HAVE_CBRTF 1/g' config.h
sed -i -e 's/#define HAVE_COPYSIGN 0/#define HAVE_COPYSIGN 1/g' config.h
sed -i -e 's/#define HAVE_TRUNC 0/#define HAVE_TRUNC 1/g' config.h
sed -i -e 's/#define HAVE_TRUNCF 0/#define HAVE_TRUNCF 1/g' config.h
sed -i -e 's/#define HAVE_RINT 0/#define HAVE_RINT 1/g' config.h
sed -i -e 's/#define HAVE_HYPOT 0/#define HAVE_HYPOT 1/g' config.h
sed -i -e 's/#define HAVE_ERF 0/#define HAVE_ERF 1/g' config.h
sed -i -e 's/#define HAVE_GMTIME_R 0/#define HAVE_GMTIME_R 1/g' config.h
sed -i -e 's/#define HAVE_LOCALTIME_R 0/#define HAVE_LOCALTIME_R 1/g' config.h
sed -i -e 's/#define HAVE_INET_ATON 0/#define HAVE_INET_ATON 1/g' config.h

6、xxxxxxxxxx error: expected ')'

錯(cuò)誤信息:

#define getenv(x) NULL
^
/home/cd008/diska/android-ndk-r9/platforms/android-18/archarm/usr/include/stdlib.h:54:14: note: in expansion of macro 'getenv'
extern char *getenv(const char *);
^
./config.h:17:19: error: expected ')' before numeric constant
#define getenv(x) NULL
^
/home/cd008/diska/android-ndk-r9/platforms/android-18/archarm/usr/include/stdlib.h:54:14: note: in expansion of macro 'getenv'
extern char *getenv(const char *);

解決: 在config.h中注釋掉#define getenv(x) NULL /#define getenv(x) NULL/

sed -i -e 's/#define getenv(x) NULL/\/\*#define getenv(x) NULL\*\//g' config.h

7、arm-linux-androideabi-ld -Wl,- soname,libavutil.so unknown option

錯(cuò)誤信息:

Users/aria/dev/android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-
4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ld -Wl,-soname

原因: gcc 構(gòu)建 .so 的命令是 -shared -wl,soname,xxxx.so 而 clang 的是 -shared -soname xxx.so

解決: 修改 ffbuild/config.mak 文件,將 SHFLAGS=-shared -Wl,-soname,$(SLIBNAME) 修改為 SHFLAGS=- shared -soname $(SLIBNAME)

以上就是Android FFmpeg音視頻解碼播放示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Android FFmpeg音視頻解碼播放的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論