百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

Android FFmpeg音视频解码播放 android ffmpeg 硬解码

xsobi 2024-12-25 16:14 1 浏览

本文分别为

  • FFmpeg简介
  • FFmpeg音视频解码播放
  • Clang编译FFmpeg常见问题

今天来说一下关于今天先说一下FFmpeg一些内容

一丶FFmpeg简介

1.简介

FFmpeg(FastForward Mpeg)是一款遵循GPL的开源软件,在音视频处理方面表现十分优秀,几乎囊括了现存所有的视音频格式的编码,解码、转码、混合、过滤及播放。同时也是一款跨平台的软件,完美兼容Linux、Windows、Mac OSX等平台。其实它由3大部件组成:

FFmpeg:由命令行组成,用于多媒体格式转换 FFplay:基于FFmpeg开源代码库libraries做的多媒体播放器 FFprobe:基于FFmpeg做的多媒体流分析器

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

音视频开发基础知识和资料包

2.FFmpeg两个强大功能

1.命令功能 2.api功能

2.1 命令功能

应用程序使用方法

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

上面举例说明了应用程序的用法,应用程序的命令行相对代码要简单很多,也能实现例如音视频分离、转码、播放等各种功能,如视频转码的命令行为:

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

这个命令用于剪切视频,-ss表示从第几秒开始,如上实例为从第5秒开始,-t代表剪持续几秒长度的视频,如上实例就是剪10秒长度的视频,copy表示视频编码格式和音频编码格式与原视频统一。

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 //分离音频流

视频解复用

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

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

视频转码

ffmpeg –i test.mp4 –vcodec h264 –s 352*278 –an –f m4v test.264 //转码为码流原始文件

ffmpeg –i test.mp4 –vcodec h264 –bf 0 –g 25 –s 352*278 –an –f m4v test.264 //转码为码流原始文件

ffmpeg –i test.avi -vcodec mpeg4 –vtag xvid –qsame test_xvid.avi //转码为封装文件

视频封装

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 开始时间,-t 持续时间

视频录制

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

YUV序列播放

ffplay -f rawvideo -video_size 1920x1080 input.yuv

YUV序列转AVI

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

常用参数说明:

主要参数:

  • -i 设定输入流
  • -f 设定输出格式
  • -ss 开始时间 视频参数:
  • -b 设定视频流量,默认为200Kbit/s
  • -r 设定帧速率,默认为25
  • -s 设定画面的宽与高 -aspect 设定画面的比例
  • -vn 不处理视频
  • -vcodec 设定视频编解码器,未设定时则使用与输入流相同的编解码器 音频参数:
  • -ar 设定采样率 -ac 设定声音的Channel数
  • -acodec 设定声音编解码器,未设定时则使用与输入流相同的编解码器 -an 不处理音频

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

C++程序员必看,抓住音视频开发的大浪潮!冲击年薪60万

二丶FFmpeg音视频解码播放

前言

通常情况下,媒体文件以如MP4,MKV、FLV等等格式存在我们的计算机,手机等设备中,而这些文件格式都属于封装格式,就是把音视频数据按照相应的规范,打包成文件。

1.FFmpeg 音视频解码流程

平常我们播放媒体文件时,通常需要经过以下几个步骤

2.FFmpeg 音视频解码原理

2.1.解协议

将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如 HTTP,RTMP,或是 MMS 等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。

解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用 RTMP 协议传输的数据,经过解协议操作后,输出 FLV 格式的数据。

2.2.解封装

将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如 MP4,MKV,RMVB,TS,FLV,AVI 等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV 格式的数据,经过解封装操作后,输出 H.264 编码的视频码流和AAC 编码的音频码流。

2.3.解码

将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如 YUV420P,RGB 等等;

2.4.音视频同步

根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。

2.5.FFmpeg音视频解码

通过前文,我们知道每一个媒体文件在被终端播放前主要经过了两个关键步骤,分别是解封装和解码。而在ffmpeg中,使用相关接口实现解封装和解码流程如下图:

由上图可知,我们需要重点关注下面这些FFmpeg的API接口:

  • av_register_all():注册所有组件。
  • avformat_open_input():打开输入视频文件。
  • avformat_find_stream_info():获取视频文件信息。
  • avcodec_find_decoder():查找解码器。
  • avcodec_open2():打开解码器。
  • av_read_frame():从输入文件读取一帧压缩数据。
  • avcodec_decode_video2():解码一帧压缩数据。


3.FFmpeg接口使用

在使用FFmpeg解码媒体文件之前,首先需要注册了容器和编解码器有关的组件。

av_register_all()


如果我们需要播放网络多媒体,则可以加载socket库以及网络加密协议相关的库,为后续使用网络相关提供支持。

avformat_network_init();

我们通过avformat_open_input()来打开一个媒体文件,并获得媒体文件封装格式的上下文

//打开一个文件并解析。可解析的内容包括:视频流、音频流、视频流参数、音频流参数、视频帧索引

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()获取媒体文件中,提取流的上下文信息,分离出音视频流。

//解码时,作用是从文件中提取流信,将所有的Stream的MetaData信息填充好,先read_packet一段数据解码分析流数据

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)=每个刻度是多少秒

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);

}

}

}

分离出音视频流之后,可以找到对应AVCodecContext,即编解码器的上下文,用来寻找对应的解码器并设置。

【更多音视频学习资料,点击下方链接免费领取↓↓,先码住不迷路~】

音视频开发基础知识和资料包

//查找对应的解码器 存储编解码器信息的结构体

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四个步骤使用的关系如下图

打开解码器之后,通过av_read_frame()一帧一帧读取压缩数据。

AVPacket \*avPacket = av\_packet\_alloc();

//读取具体的音/视频帧数据

int ret = av_read_frame(pAVFormatCtx, avPacket);

if (ret==0){

//stream_index:标识该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解码一帧视频或者音压缩数据,通过AVPacket->AVFrame得到视频像素数据。

//解码AVPacket->AVFrame

ret = avcodec_decode_audio4(pCodeCtx, frame, &got_frame, packet);

//解码一帧视频压缩数据,得到视频像素数据

ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);


三丶Clang编译FFmpeg常见问题

1、命令找不到

错误信息:

./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网上的shell脚本,可能会是dos格式,请使用dos2unix build_android.sh 转换一下,删掉多余空格(这一点非常重要dos2unix 是一个工具,如果没有安装的话请先安装一下:brew install dos2unix ,很快就完事。

2.xmakefile 文件没有生成

错误信息:

./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

解决: 执行 ./configure --disable-x86asm 生成config.mak文件

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

create an executable file

错误信息:

/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 开始,已经移除了gcc这个编译工具详情见ndk r18b修订内容 解决: 使用clang进行编译

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 ')'

错误信息:

#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

错误信息:

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 构建 .so 的命令是 -shared -wl,soname,xxxx.so 而 clang 的是 -shared -soname xxx.so

解决: 修改 ffbuild/config.mak 文件,将 SHFLAGS=-shared -Wl,-soname,$(SLIBNAME) 修改为 SHFLAGS=- shared -soname $(SLIBNAME)

相关推荐

【互联网那些事】高效开发Android App的10个建议

  假如要GooglePlay上做一个最失败的案例,那最好的秘诀就是界面奇慢无比、耗电、耗内存。接下来就会得到用户的消极评论,最后名声也就臭了。即使你的应用设计精良、创意无限也没用。  耗电或者内存...

手机APP开发方式有哪些? 手机app的开发模式有哪三种?

微信小程序开发定制_软件开发_APP开发_网站制作-优软软件开发...

Android开发入门(一):Android系统简介

Android系统是Google公司在2008年推出的一款智能移动设备操作系统,通过不断地版本迭代,目前已经推出到Android11版本了。Android系统广泛应用在手机、平板、电视等各种电子设...

物联网app开发流程 物联网app开发工具

现在随着科技的发展,很多产品都想用一个手机app去显示他的参数数据或者通过手机app去控制它。但是很多人不知道他的流程。今天我就来说下物联网app开发流程。首先需要把物联网app开发流程分2个步骤,一...

Android开发进阶 | 如何学习 Android Framework?

大部分有“如何学习Framework源码”这个疑问的,应该大都是应用层开发。应用层是被Framework层调用执行的,知道自己的代码是怎么被调用的,才能理解程序的本质,理解本质有助于解决遇到的...

快速实现APP混合开发(Hybrid App开发)攻略

前言:...

三个阶段带你了解一款app开发的完整流程

第一个阶段需求阶段:1.需求讨论--开发类型、开发平台、具体的产品功能需求、项目预计完成时间、预算2.需求评估--确认合作后评估具体的预算3.界面设计--设计部门进行产品界面设计,形成效果图...

Android 开发中文引导-应用小部件

应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截...

手机软件开发从零开始【Android第2篇Hello】

Hello,朋友们我们又见面了。上一篇我们讲到了《Android开发环境搭建【Android基础第1篇】》,错过的朋友可以点击文章末尾的“阅读原文”查看。另外需要下载JDK和ADT-bundle工具的...

「全栈工程师之梦的开始--安卓开发(二)」开发安卓app

在配置好jdk开发环境、安装好开发工具Androidstudio后,我们就可以开始开发安卓app了。首先,我们需要先了解下android的术语。...

二、Android界面开发 android 开发

学习目标了解Android常用布局了解Android常用控件...

如何开发一款APP既快捷也简便 开发一款app的步骤

具体较为简单的步骤可以选择用androidstudio开发app1、打开软件,在菜单中选择file-》newproject打开创建向导。2、配置项目,确定各个名称和存放项目的存放路径;Applic...

安卓开发中的“Android高手”,需要具备哪些技术?

前言成为一名安卓开发者很容易,但是要成为一名“Android高手”却不那么容易;...

移动开发(一):使用.NET MAUI开发第一个安卓APP

对于工作多年的C#程序员来说,近来想尝试开发一款安卓APP,考虑了很久最终选择使用.NETMAUI这个微软官方的框架来尝试体验开发安卓APP,毕竟是使用VisualStudio开发工具,使用起来也...

微软推出PowerApps:零基础开发Win10/iOS/安卓企业应用

IT之家讯微软今天面向企业宣布了全新的应用开发解决方案PowerApps,让Windows(包括Win10)、iOS以及安卓应用的开发和分发变得更加简单。PowerApps的用户界面与Office办...