![ed22e4ce3763953d6d7cb8faf1b04dce.png](https://img-blog.csdnimg.cn/img_convert/ed22e4ce3763953d6d7cb8faf1b04dce.png)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
本篇文章将通过 音视频解码与音视频同步来介绍FFmpeg:
一、FFmpeg 音视频解码
封装格式
我们经常所说的视频格式,如 mp4 、 mkv 、 rmvb 、flv 等,表示的是音视频的封装格式,封装格式实质上是把音频数据、视频数据和字幕数据打包成一个文件的规范。从技术的角度来讲,优秀的音视频封装格式应该支持大多数音视频编码标准。
主要的封装格式:
![f71188b07f6da25fa9942f2686652de6.png](https://img-blog.csdnimg.cn/img_convert/f71188b07f6da25fa9942f2686652de6.png)
编码格式
编码的目的在于通过压缩算法降低数据量,提高数据的存储和传输效率。视频编码是将视频像素数据( RGB , YUV 等)压缩成为视频码流。音频编码是将音频采样数据( PCM 等)压缩成为音频码流。
主要视频编码格式:
![6cfad9a1500599f63c54e0469e92af33.png](https://img-blog.csdnimg.cn/img_convert/6cfad9a1500599f63c54e0469e92af33.png)
主要音频编码格式:
![c3d9c44f38825b757657b7da6997c5c4.png](https://img-blog.csdnimg.cn/img_convert/c3d9c44f38825b757657b7da6997c5c4.png)
音视频解码流程
![3e1e4553f1186d496a889805fa61403d.png](https://img-blog.csdnimg.cn/img_convert/3e1e4553f1186d496a889805fa61403d.png)
- 解封装格式。将输入的按照一定格式封装的音视频数据,分离成为音频流压缩编码数据和视频流压缩编码数据。
- 解码。将视频和音频的压缩编码数据,解码成为非压缩的视频和音频原始数据。视频压缩数据通过解码输出为像素数据,如 YUV420P 、 RGB 等;音频压缩数据通过解码输出为非压缩的音频抽样数据,如 PCM 数据。
- 音视频同步。同步解码出来的视频和音频数据,并将音视频数据送至系统的声卡和显卡,播放和显示出来。
FFmpeg 函数库
FFmpeg 一般有 8 个函数库,各个函数库的功能如下:
![ce4047c7e056c13f9d7a7e8a7b1e977b.png](https://img-blog.csdnimg.cn/img_convert/ce4047c7e056c13f9d7a7e8a7b1e977b.png)
FFmpeg 音视频解码
FFmpeg 音视频解码主要流程代码描述:
1. av_register_all() //注册组件
2. avformat_alloc_context //获取封装格式上下文
3. avformat_find_stream_info //获取输入文件信息
4. avcodec_find_decoder //获取解码器
5. avcodec_open2 //打开解码器
6. avcodec_decode_video2 或 avcodec_decode_audio4 //解码音视频帧
在 AS 工程中引入 FFmpeg 8 个动态库和 libyuv (负责视频像素数据格式转换)动态库。
工程的头文件目录:
![7d3df1f5acfdfec21b2708cfa03be8dd.png](https://img-blog.csdnimg.cn/img_convert/7d3df1f5acfdfec21b2708cfa03be8dd.png)
工程的动态库目录:
![7d3df1f5acfdfec21b2708cfa03be8dd.png](https://img-blog.csdnimg.cn/img_convert/7d3df1f5acfdfec21b2708cfa03be8dd.png)
Java 层 API :
package com.haohao.ffmpeg;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
import android.view.Surface;
/**
* author: haohao
* time: 2017/12/19
* mail: haohaochang86@gmail.com
* desc: AVUtils
*/
public class AVUtils {
private static final String TAG = "AVUtils";
private static AVCallback AVCallback;
private static AVCallback sAVCallback;
public static void registerCallback(AVCallback callback) {
sAVCallback = callback;
}
static {
System.loadLibrary("avfilter-5");
System.loadLibrary("avdevice-56");
System.loadLibrary("yuv");
System.loadLibrary("avutil-54");
System.loadLibrary("swresample-1");
System.loadLibrary("avcodec-56");
System.loadLibrary("avformat-56");
System.loadLibrary("swscale-3");
System.loadLibrary("postproc-53");
System.loadLibrary("native-lib");
}
/**
* 解码视频中的视频压缩数据
* @param input_file_path 输入的视频文件路径
* @param output_file_path 视频压缩数据解码后输出的 YUV 文件路径
*/
public static native void videoDecode(String input_file_path, String output_file_path);
/**
* 显示视频视频解码后像素数据
* @param input 输入的视频文件路径
* @param surface 用于显示视频视频解码后的 RGBA 像素数据
*/
public static native void videoRender(String input, Surface surface);
/**
* 解码视频中的音频压缩数据
* @param input 输入的视频文件路径
* @param output 音频压缩数据解码后输出的 PCM 文件路径
*/
public static native void audioDecode(String input, String output);
/**
* 播放视频中的音频数据
* @param input 输入的视频文件路径
*/
public static native void audioPlay(String input);
/**
* 创建一个 AudioTrack 对象,用于播放音频,在 Native 层中调用。
*/
public static AudioTrack createAudioTrack(int sampleRate, int num_channel) {
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
Log.i(TAG, "声道数:" + num_channel);
int channelConfig;
if (num_channel == 1) {
channelConfig = android.media.AudioFormat.CHANNEL_OUT_MONO;
} else if (num_channel == 2) {
channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;
} else {
channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;
}
int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
AudioTrack audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
sampleRate, channelConfig,
audioFormat,
bufferSize, AudioTrack.MODE_STREAM);
return audioTrack;
}
public interface AVCallback {
void onFinish();
}
}
MySurfaceView.java
/**
* author: haohao
* time: 2017/12/20
* mail: haohaochang86@gmail.com
* desc: MySurfaceView
*/
public class MySurfaceView extends SurfaceView {
public MySurfaceView(Context context) {
super(context);
}
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(){
// 设置像素绘制格式为 RGBA_8888
SurfaceHolder holder = getHolder();
holder.setFormat(PixelFormat.RGBA_8888);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.haohao.ffmpeg.MySurfaceView
android:id="@+id/my_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0.7"
android:orientation="horizontal">
<Button
android:id="@+id/video_decode_btn"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="视频解码" />
<Button
android:id="@+id/video_render_btn"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="视频渲染" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha