您的位置:首页 > 其它

视频的「编解码」与「传输」的那些事儿

2017-11-24 00:00 316 查看


本文来自作者 Owen Chan 在 GitChat 上分享「关于视频的编解码与传输技术,你想知道的都在这里」,「阅读原文」查看交流实录

「文末高能」

编辑 | 泰龙


一、如何编译 FFmpag


准备工作

下载 FFmpeg 源码 :https://www.ffmpeg.org

下载 NDK  :http://developer.android.com/ndk/downloads/index.html

下载后文件在 Mac 中的存放路径如下:
ChendeMacBook-Pro:  compileFF
ChendeMacBook−Pro:compileFFchenzongwen pwd
/Users/chenzongwen/compileFF   //文件的存放路径 NDK 与FFmpeg 源码


如何编译 ffmpeg

首先进入 ffmpeg-3.0文件夹 ,在文件夹中增加 编译脚本 ffmpegConfig 文件如下图:



ffmpegConfig 文件内容如下:
#!/bin/bash

NDK=/Users/chenzongwen/compileFF/android-ndk-r11b
export PATH=$PATH:$NDK
SYSROOT=$NDK/platforms/android-19/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
function build_one
{
bash  ./configure \
   --prefix=$PREFIX \
   --enable-shared \
   --disable-static \
   --disable-doc \
   --disable-ffserver \
   --enable-cross-compile \
   --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
   --target-os=linux \
   --arch=arm \
   --extra-libs=-lgcc \
   --sysroot=$SYSROOT \
   --enable-asm \
   --enable-neon \
   --extra-cflags="-O3 -fpic $ADDI_CFLAGS" \
   --extra-ldflags="$ADDI_LDFLAGS" \
   $ADDITIONAL_CONFIGURE_FLAG
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm -mfpu=neon"
build_one
~                                                                                                                                                            
~        

然后在当前文件夹下执行如下命令:

chenzongwen. / ffmpegConfig

chenzongwen make -j4  //4 为 用4个 cup 进行编译

chenzongwen$ make install

命令执行完之后会在当前文件夹下生成一个 android 文件夹 android/arm 文件夹中的内容如下几个文件夹:
bin
include
lib
share

include 中存放的是头文件, lib 中存放的是 so 文件 整个编译过程结束 。


如何使用 FFmpeg

在跟 anroid-ndk-r11b ffmpeg-3.0 同级的目录下 创建 jni 文件夹 执行如下命令 :mkdir  jni

将之前编译的头文件(在 include 文件夹下)拷贝到 jni 文件夹下 并且在 jni 文件夹下创建 prbuilt 文件夹 并将之前生成的 so(在 lib 文件夹下)拷贝到 prebuilt 文件夹下, 拷贝完成后如下图:
ChendeMacBook-Pro:jni chenzongwen$ ls
libavcodec  libavdevice libavfilter libavformat libavutil   libswresample   libswscale  prebuild
ChendeMacBook-Pro:jni chenzongwen$ ls prebuild/
libavcodec-57.so    libavfilter-6.so    libavutil-55.so  libswscale-4.so
libavdevice-57.so   libavformat-57.so   libswresample-2.so  libswscale.so


调用 so 方法 在当前文件夹下添加两个文件 Android.mk 和 Application.mk 内容分别如下

Android.mk 内容
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec-56-prebuilt
LOCAL_SRC_FILES := prebuilt/libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)

#include $(CLEAR_VARS)
#LOCAL_MODULE := avdevice-56-prebuilt
#LOCAL_SRC_FILES := prebuilt/libavdevice-57.so
#include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avfilter-5-prebuilt
LOCAL_SRC_FILES := prebuilt/libavfilter-6.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avformat-56-prebuilt
LOCAL_SRC_FILES := prebuilt/libavformat-57.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avutil-54-prebuilt
LOCAL_SRC_FILES := prebuilt/libavutil-55.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avswresample-1-prebuilt
LOCAL_SRC_FILES := prebuilt/libswresample-2.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  swscale-3-prebuilt
LOCAL_SRC_FILES := prebuilt/libswscale-4.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

Application.mk 的内容如下 :

ChendeMacBook-Pro:jni chenzongwen$ vim Application.mk
APP_ABI := armeabi
#APP_ABI := armeabi-v7a
APP_PLATFORM := android-10


编写调用文件 在当前文件夹下创建 **.c 文件 内容如下(文章结束会给出全部源码):
JNIEXPORT jint JNICALL Java_tan_h264_FFmpegNative_decode_1file
 (JNIEnv *env, jobject obj, jstring filePath) {   // jni 方法的定义
 //todu
}

JNIEXPORT jint JNICALL Java_tan_h264_FFmpegNative_decodeFrame
 (JNIEnv *env, jobject obj, jbyteArray in, jint inSize)
{
//todo
}

开始编译 在 jni 目录下执行:
ChendeMacBook-Pro:jni chenzongwen$ ../android-ndk-r11b/ndk-build

最后将 如下目录下的 so 拷贝到工程中就可以使用了。
ChendeMacBook-Pro:armeabi chenzongwen$ pwd
/Users/chenzongwen/compileFF/libs/armeabi
ChendeMacBook-Pro:armeabi chenzongwen$ ls
libavcodec-57.so    libavutil-55.so  libswscale-4.so
libavfilter-6.so    libowenchan_Test.so
libavformat-57.so   libswresample-2.so
ChendeMacBook-Pro:armeabi chenzongwen$


二、Android App 中如何调用 FFmpag so, jni 技术的讲解

java 层代码调用如下:
import android.graphics.Bitmap;

import java.nio.ByteBuffer;

public class FFmpegNative {
   static {
       System.loadLibrary("avutil-54");
       System.loadLibrary("swresample-1");
       System.loadLibrary("avcodec-56");
       System.loadLibrary("avformat-56");
       System.loadLibrary("swscale-3");
       System.loadLibrary("avfilter-5");
       System.loadLibrary("avdevice-56");
       System.loadLibrary("ffmpeg_codec");
   }

   public native int decode_init();

   public native int decode_file(String filePath);

   public native int decodeFrame(ByteBuffer in, int inSzie);

   public native int decodeFrame2(ByteBuffer in, int inSzie);

   public native int copyFrameRGB(ByteBuffer out);

   public native int copyFrameYUV420p(ByteBuffer out);

   public native int copyFrame2(ByteBuffer outY, ByteBuffer outU, ByteBuffer outV);
}


三、H264 格式


H.264分为两层

(一) H264 分为两层

视频编码层 (VCL: Video Coding Layer): 进行视频编解码,包括运动补偿预 测,变换编码和熵编码等功能;

网络 取层 (NAL: Network Abstraction Layer): 用于采用适当的格式对VCL 视频数据进行封装打包;VCL需要打包成NAL,才能用于传输或存储.

(二)分层的目的

可以定义 VCL 视频压缩处理与 NAL 网络传输机制的接口,这样允许视频 编码层 VCL 的设计可以在不同的处理器平台进行移植,而与NAL层的数据封装格 式无关;

VCL 和 NAL 都被设计成工作于不同的传输环境,异构的网络环境并不需要 对 VCL 比特流进行重构和重编码。

(三)NALU 单元(NAL Unit)

H264 基本码流由一系列的 NALU 组成,组成结构如下



NALU:  Coded H.264 data is stored or transmitted as a series of packets known as Network Abstraction LayerUnits. (NALU单元)

RBSP : A NALU contains a Raw Byte Sequence Payload, a sequence of bytes containing syntax elements.(原始数据字节流)

SODB:String Of Data Bits (原始数据比特流, 长度不一定是8的倍数,需要补齐)

Start code

一共有两种起始码:3字节的 0x000001 和4字节的 0x00000001; 

如果 NALU 对应的 Slice 为一帧的开始,则用4字节表示,即 0x00000001; 

否则用3字节 0x000001 表示,就是一个完整的帧被编为多个slice的时候,包含这些 slice 的 nalu 使用3字节起始码。

由于NAL的语法中没有给出长度信息,实际的传输、存储系统需要增加额外 的起始头实现各个 NAL 单元的定界。

先识别 H264 起始码 0x00000001;

接着读取NALU的header 字节,判断后 RBSP类型,相应的 六进制类 型定义如下:
0x67: SPS
0x68: PPS
0x65: IDR
0x61: non-IDR Slice 0x01: B Slice
0x06: SEI
0x09: AU Delimiter

从 读出的 个 H.264 视频帧以下 的形式存在: 0000000167… SPS
0000000168... PPS
00 00 00 01 65 ... IDR Slice



剩下的几个部分是视频的传输压缩与解压,我做 Chat 交流的时候对着代码来分析。

代码下载地址:https://github.com/chenzongwen/SimpleFFmpeg

界面如下:



近期热文

《手把手教你如何向
Linux 内核提交代码》


《Java
实现 Web 应用中的定时任务》


沉迷前端,无法自拔的人,如何规划职业生涯?

TensorFlow
计算与智能基础


突破技术发展瓶颈、成功转型的重要因素

Selenium
爬取评论数据,就是这么简单!


你和普通的程序员最大区别

在于




[b]「阅读原文」[/b]看交流实录,你想知道的都在这里
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: