返回顶部
首页 > 资讯 > 移动开发 >Android 音视频之FFmpeg
  • 334
分享到

Android 音视频之FFmpeg

ffmpegAndroid 2022-06-06 14:06:34 334人浏览 独家记忆
摘要

FFmpeg介绍 FFmpeg是一套可以用来记录、处理数字音频、视频,并将其转换为流的开源框架,采用LPL或GPL许可证,提供了录制、转换以及流

FFmpeg介绍

FFmpeg是一套可以用来记录、处理数字音频、视频,并将其转换为流的开源框架,采用LPL或GPL许可证,提供了录制、转换以及流化音视频的完整解决方案。它的可移植性或者说跨平台特性非常强大。

默认的编译会生成4个可执行文件和8个静态库。 可执行文件包括用于转码、推流、Dump媒体文件的ffmpeg、用于播放媒体文件的ffplay、用于获取媒体文件信息的ffprobe,以及作为简单流媒体服务器ffserverAVUtil:核心工具库,该模块是最基础的模块之一,下面的许多其他模块都会依赖该库做一些基本的音视频处理操作。 AVFormat:文件格式和协议库,该模块是最重要的模块之一,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。 AVCodec:编解码库,该模块也是最重要的模块之一,封装了Codec层,但是有一些Codec是具备自己的License的,FFmpeg是不会默认添加像libx264、FDK-AAC、lame等库的,但是FFmpeg就像一个平台一样,可以将其他的第三方的Codec以插件的方式添加进来,然后为开发者提供统一的接口。 AVFilter:音视频滤镜库,该模块提供了包括音频特效和视频特效的处理,在使用FFmpeg的api进行编解码的过程中,直接使用该模块为音视频数据做特效处理是非常方便同时也非常高效的一种方式。 AVDevice:输入输出设备库,比如,需要编译出播放声音或者视频的工具ffplay,就需要确保该模块是打开的,同时也需要libSDL的预先编译,因为该设备模块播放声音与播放视频使用的都是libSDL库。 SwrRessample:该模块可用于音频重采样,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换。 SWScale:该模块是将图像进行格式转换的模块,比如,可以将YUV的数据转换为RGB的数据。 PostProc:该模块可用于进行后期处理,当我们使用AVFilter的时候需要打开该模块的开关,因为Filter中会使用到该模块的一些基础函数。 ffmpeg术语 容器/文件(Conainer/File):即特定格式的多媒体文件,比如MP4、flv、mov等。 媒体流(Stream):表示时间轴上的一段连续数据,如一段声音数据、一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器。 数据帧/数据包(Frame/Packet):通常,一个媒体流是由大量的数据帧组成的,对于压缩数据,帧对应着编解码器的最小处理单元,分属于不同媒体流的数据帧交错存储于容器之中。 编解码器:编解码器是以帧为单位实现压缩数据和原始数据之间的相互转换的。
AVFormatContext就是对容器或者说媒体文件层次的一个抽象,该文件中(或者说在这个容器里面)包含了多路流(音频流、视频流、字幕流等)对流的抽象就是AVStream;在每一路流中都会描述这路流的编码格式,对编解码格式以及编解码器的抽象就是AVCodecContextAVCodec;对于编码器或者解码器的输入输出部分,也就是压缩数据以及原始数据的抽象就是AVPacketAVFrameffmeg 使用步骤

FFmpeg中最重要的几个模块都已经介绍完毕了,下面来具体看一个解码的实例,该实例实现的功能非常单一,就是把一个视频文件解码成为单独的音频PCM文件和视频YUV文件

首先,要使用FFmpeg就必须要引用它的头文件,以及在链接阶段使用它的静态库文件如果是在iOS下,那么可直接以下面这种方式引用头文件:

#include "libavfORMat/avformat.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/pixdesc.h"
如果是在Androidc++环境下,那么可直接以下面这种方式引用头文件:
extern "C" {
	 #include "3rdparty/ffmpeg/include/libavformat/avformat.h"
	 #include "3rdparty/ffmpeg/include/libswscale/swscale.h" 
	 #include "3rdparty/ffmpeg/include/libswresample/swresample.h"
	 #include "3rdparty/ffmpeg/include/libavutil/pixdesc.h"
 }

extern“C”的解释
作为一种面向对象的语言,C++支持函数的重载,而面向过程的C语言是不支持函数重载的。同一个函数在C++中编译后与其在C中编译后,在符号表中的签名是不同的,假如对于同一个函数:void decode(float position, float duration)在C语言中编译出来的签名是_decoder,而在C++语言中,一般编译器的生成则类似于_decode_float_float。虽然在编译阶段是没有问题的,但是在链接阶段,如果不加extern“C”关键字的话,那么将会链接_decoder_float_float这个方法签名;而如果加了extern“C”关键字的话,那么寻找的方法签名就是_decoder。而FFmpeg就是C语言书写的,编译FFmpeg的时候所产生的方法签名都是C语言类型的签名,所以在C++中引用FFmpeg必须要加extern“C”关键字。

可以看到,引用头文件的方式是不同的,因为每个平台配置的Header Search Path是不一样的,在iOS的IDE Xcode开发中,可以在工程文件的配置中修改Header Search Path;在Android的底层开发中,可以配置makefile文件中的内置变量LOCAL_C_INCLUDES来指定头文件的搜索路径,当然如果要在跨平台(Android平台和iOS平台)的模块(以C++语言编写)中引用FFmpeg的头文件,则需要编写一个platform_4_ffmpeg.h,并在其中根据各个平台预定义的宏去编译不同的引用方式,代码如下:

#ifdef __ANDROID__
 extern "C" {
	  #include "3rdparty/ffmpeg/include/libavformat/avformat.h" 
	  #include "3rdparty/ffmpeg/include/libswscale/swscale.h" 
	  #include "3rdparty/ffmpeg/include/libswresample/swresample.h" 
	  #include "3rdparty/ffmpeg/include/libavutil/pixdesc.h" 
  }#elif defined(__APPLE__) // iOS或OS X 
  extern "C" { 
	  #include "libavformat/avformat.h"
	  #include "libswscale/swscale.h" 
	  #include "libswresample/swresample.h" 
	  #include "libavutil/pixdesc.h" 
  }#endif

注册协议、格式与编解码器
使用FFmpeg的API,首先要调用FFmpeg的注册协议、格式与编解码器的方法,确保所有的格式与编解码器都被注册到了FFmpeg框架中,当然如果需要用到网络的操作,那么也应该将网络协议部分注册到FFmpeg框架,以便于后续再去查找对应的格式。代码如下:

avformat_network_init();
av_reGISter_all();

打开媒体文件源,并设置超时回调
注册了格式以及编解码器之后,接下来就应该打开对应的媒体文件了,当然该文件既可能是本地磁盘的文件,也可能是网络媒体资源的一个链接,如果是网络链接,则会涉及不同的协议,比如RTMP、Http等协议的视频源。打开媒体资源以及设置超时回调的代码如下:

AVFormatContext *formatCtx = avformat_alloc_context();
AVIOInterruptCB int_cb = {interrupt_callback, (__bridge void *)(self)};
formatCtx->interrupt_callback = int_cb;
avformat_open_input(formatCtx, path, NULL, NULL);
avformat_find_stream_info(formatCtx, NULL);

寻找各个流,并且打开对应的解码器
上一步中已打开了媒体文件,相当于打开了一根电线,这根电线里面其实还有一条红色的线和一条蓝色的线,这就和媒体文件中的流非常类似了,红色的线代表音频流,蓝色的线代表视频流。所以这一步我们就要寻找出各个流,然后找到流中对应的解码器,并且打开它。
寻找音视频流:

for(int i = 0; i nb_streams; i++) {
	AVStream* stream = formatCtx->streams[i];
	if(AVMEDIA_TYPE_VIDEO == stream->codec->codec_type) {
		// 视频流
		videoStreamIndex = i;
	} else if(AVMEDIA_TYPE_AUDIO == stream->codec->codec_type ){
		// 音频流
		audioStreamIndex = i;	
	}
}
打开音频流解码器:
AVCodecContext * audiocodecCtx = audioStream->codec;
AVCodec *codec = avcodec_find_decoder(audioCodecCtx ->codec_id);
if(!codec){
	// 找不到对应的音频解码器
}
int openCodecErrCode = 0;
if ((openCodecErrCode = avcodec_open2(codecCtx, codec, NULL)) codec;
AVCodec *codec = avcodec_find_decoder(videoCodecCtx->codec_id);
if(!codec) {
	// 找不到对应的视频解码器
}
int openCodecErrCode = 0;
if ((openCodecErrCode = avcodec_open2(codecCtx, codec, NULL)) < 0) {
	// 打开视频解码器失败
}

初始化解码后数据的结构体
知道了音视频解码器的信息之后,下面需要分配出解码之后的数据所存放的内存空间,以及进行格式转换需要用到的对象。
构建音频的格式转换对象以及音频解码后数据存放的对象:

SwrContext *swrContext = NULL;
if(audioCodecCtx->sample_fmt != AV_SAMPLE_FMT_S16) {
	// 如果不是我们需要的数据格式
	swrContext = swr_alloc_set_opts(NULL, outputChannel, AV_SAMPLE_FMT_S16, outSampleRate, in_ch_layout, in_sample_fmt, in_sample_rate, 0, NULL);
	if(!swrContext || swr_init(swrContext)) {
		if(swrContext) {
			swr_free(&swrContext);	
		}
	}
	audioFrame = avcodec_alloc_frame();
}
构建视频的格式转换对象以及视频解码后数据存放的对象:
AVPicture picture;
bool pictureValid = avpicture_alloc(&picture, PIX_FMT_YUV420P, videoCodecCtx->width, videoCodecCtx->height) == 0;
if (!pictureValid){
	// 分配失败
	return false;
}
	swsContext = sws_getCachedContext(swsContext, videoCodecCtx->width, videoCodecCtx->height, videoCodecCtx->pix_fmt, videoCodecCtx->width, videoCodecCtx->height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL);
	videoFrame = avcodec_alloc_frame();
}

读取流内容并且解码
打开了解码器之后,就可以读取一部分流中的数据(压缩数据),然后将压缩数据作为解码器的输入,解码器将其解码为原始数据(裸数据),之后就可以将原始数据写入文件了:

AVPacket packet;
int GotFrame = 0;
while(true) {
	if(av_read_frame(formatContext, &packet)) {
		// End Of File break;
	}
	int packetStreamIndex = packet.stream_index;
	if(packetStreamIndex == videoStreamIndex) {
		int len = avcodec_decode_video2(videoCodecCtx, videoFrame, &gotFrame, &packet);
		if(len handleVideoFrame();
		}
	} else if(packetStreamIndex == audioStreamIndex) {
		int len = avcodec_decode_audio4(audioCodecCtx, audioFrame, &gotFrame, &packet);
		if(len handleVideoFrame();
		}
	}
}

处理解码后的裸数据
解码之后会得到裸数据,音频就是PCM数据,视频就是YUV数据。下面将其处理成我们所需要的格式并且进行写文件。
音频裸数据的处理:

void* audioData;
int numFrames;
if(swrContext) {
	int bufSize = av_samples_get_buffer_size(NULL, channels, (int)(audioFrame->nb_samples * channels), AV_SAMPLE_FMT_S16, 1);
	if (!_swrBuffer || _swrBufferSize nb_samples * channels), (const uint8_t **)_audioFrame->data, audioFrame->nb_samples);
	audioData = swrBuffer;
} else {
	audioData = audioFrame->data[0];
	numFrames = audioFrame->nb_samples;
}

关闭所有资源
解码完毕之后,或者在解码过程中不想继续解码了,可以退出程序,当然,退出的时候,要将用到的FFmpeg框架中的资源,包括FFmpeg框架对外的连接资源等全都释放掉。
关闭音频资源:

if (swrBuffer) {
	free(swrBuffer);
	swrBuffer = NULL;
	swrBufferSize = 0;
}
if (swrContext) {
	swr_free(&swrContext);
	swrContext = NULL;
}
if (audioFrame) {
	av_free(audioFrame);
	audioFrame = NULL;
}
if (audioCodecCtx) {
	avcodec_close(audioCodecCtx);
	audioCodecCtx = NULL;
}
关闭视频资源:
if (swsContext) {
	sws_freeContext(swsContext);
	swsContext = NULL;
}
if (pictureValid) {
	avpicture_free(&picture);
	pictureValid = false;
}
if (videoFrame){ 
	av_free(videoFrame);
	videoFrame = NULL;
}
if (videoCodecCtx) {
	avcodec_close(videoCodecCtx);
	videoCodecCtx = NULL;
}
关闭连接资源:
if (formatCtx) {
	avformat_close_input(&formatCtx);
	formatCtx = NULL;
}
Arvin_FM 原创文章 15获赞 21访问量 3616 关注 私信 展开阅读全文
作者:Arvin_FM


--结束END--

本文标题: Android 音视频之FFmpeg

本文链接: https://lsjlt.com/news/29758.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android 音视频之FFmpeg
    FFmpeg介绍 FFmpeg是一套可以用来记录、处理数字音频、视频,并将其转换为流的开源框架,采用LPL或GPL许可证,提供了录制、转换以及流...
    99+
    2022-06-06
    ffmpeg Android
  • PHP-FFMpeg 操作音视频
    ✨ 目录 🎈 安装PHP-FFMpeg🎈 视频中提取一张图片🎈 视频中提取多张图片🎈 调整视频大小🎈 ...
    99+
    2023-10-21
    php ffmpeg 音视频 经验分享
  • Qt音视频开发之实现ffmpeg视频旋转显示
    目录一、前言二、效果图三、体验地址四、相关代码五、功能特点5.1 基础功能5.2 特色功能5.3 视频控件一、前言 用手机或者平板拍摄的视频文件,很可能是旋转的,比如分辨率是1280...
    99+
    2023-03-22
    Qt ffmpeg视频旋转显示 Qt 视频旋转显示 Qt ffmpeg视频 Qt ffmpeg
  • Qt音视频开发之怎么实现ffmpeg视频旋转显示
    这篇文章主要介绍了Qt音视频开发之怎么实现ffmpeg视频旋转显示的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Qt音视频开发之怎么实现ffmpeg视频旋转显示文章都会有所收获,下面我们一起来看看吧。一、前言用...
    99+
    2023-07-05
  • Android 之 MediaPlayer 播放音频与视频
    本节引言: 本节带来的是Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频 该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码 和播放音视频。它支持三种不同的...
    99+
    2023-10-04
    android 音视频
  • 音视频开发---ffmpeg rtmp推流
    1、推流介绍 推流是将输入视频数据推送至流媒体服务器, 输入视频数据可以是本地视频文件(avi,mp4,flv......),也可以是内存视频数据,或者摄像头等系统设备,也可以是网络流URL。本篇介绍将本地视频文件通过FFmpeg编程...
    99+
    2023-09-17
    音视频 服务器 Powered by 金山文档
  • 【FFmpeg实战】Flutter音视频裁剪
    作者:JianLee 链接:https://www.jianshu.com/p/868c8536a9b2 flutter_ffmpeg是什么? ffmpeg是一个音视频处理库,通过命令行的形式,...
    99+
    2023-10-23
    ffmpeg flutter 音视频
  • 怎么利用FFmpeg合并音频和视频
    这篇文章主要介绍了怎么利用FFmpeg合并音频和视频的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么利用FFmpeg合并音频和视频文章都会有所收获,下面我们一起来看看吧。一、FFmpeg 多个音频合并的2种方...
    99+
    2023-07-05
  • Android NDK开发之FFmpeg视频添加水印
    目录前言1.FFmpeg添加水印命令1.1.水印命令1.2.命令补充说明2.Android 核心代码2.1.jni Java声明2.2.核心代码ffmpeg.c2.3调用程序3.运行...
    99+
    2024-04-02
  • Android提高之MediaPlayer音视频播放
    前面文章已经详细介绍了Android界面的入门技术,相信大家在看完和跟着练习之后,会对于常用的Layout和View都会有一定的了解了,接下来就不再强调介绍界面了,而是针对具体...
    99+
    2022-06-06
    Android
  • Qt利用ffmpeg实现音视频同步
    目录一、前言二、效果图三、体验地址四、相关代码五、功能特点5.1 基础功能5.2 特色功能5.3 视频控件5.4 内核ffmpeg一、前言 用ffmpeg来做音视频同步,个人认为这个...
    99+
    2023-01-04
    Qt ffmpeg音视频同步 Qt 音视频同步 Qt ffmpeg
  • Android音视频开发(一)音视频基础知识
    前言 最近工作方面没有太多事,所以难得有些空闲时间,针对当前音视频app的流行,为了不让自己淘汰,提升自己的专业能力,于是决定学习安卓平台音视频开发相关知识,然而自己这方面却是...
    99+
    2022-06-06
    Android
  • FFmpeg 音频可视化解码流程详解
    目录一、解码流程1.1、解析音频信息1.2、从原始数据packet到frame1.3、从frame到PCM byte二、分贝计算三、实现效果一、解码流程 解码流程大致分为以下三个部分...
    99+
    2024-04-02
  • Android开发之音视频协议介绍
    目录什么是视频文件什么是264了解音视频协议有啥用?两大电信联盟ITU-TISOITU-T 视频编码发展历程H.26X系列(由ITU[国际电传视讯联盟]主导)其他音视频协议Googl...
    99+
    2024-04-02
  • Android开发之音视频协议分析
    这篇文章主要介绍“Android开发之音视频协议分析”,在日常操作中,相信很多人在Android开发之音视频协议分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android开发之音视频协议分析”的疑惑有所...
    99+
    2023-06-30
  • Python使用ffmpeg合成视频、音频的实现方法
    最近有在使用屏幕录制软件录制桌面,在用的过程中突发奇想,使用python能不能做屏幕录制工具,也锻炼下自己的动手能力。接下准备写使用python如何做屏幕录制工具的系列文章: 录制屏...
    99+
    2024-04-02
  • Android 音视频开发—MediaPlayer音频与视频的播放介绍
    Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。 它支持...
    99+
    2023-09-21
    android 音视频
  • Android音视频开发(五)AudioRecord录制音频
    简介 AudioRecord是安卓多媒体框架中用于录制音频的工具。它支持录制原始音频数据,即PCM数据,PCM数据不能被播放器直接播放,需要编码...
    99+
    2022-06-06
    Android
  • Android视频加水印之FFmpeg的简单应用实例
    视频处理是Android开发中常见的需求。像是视频加水印,视频格式转换,视频截图等等…… FFmpeg是处理视频时常用到的工具,一般情况可以用FFm...
    99+
    2022-06-07
    ffmpeg Android
  • Android音视频编码(四)
    音视频不同步问题 说明: 视频录制时,音频流和视频流都是通过设置的pts来确定播放时的具体时间顺序;正常情况下设置好每一帧的pts,即可保证播放...
    99+
    2022-06-06
    编码 视频编码 Android
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作