编译
编译的时候可以指定这个参数
gn gen out/Debug --args='target_os="android" target_cpu="arm" rtc_use_h264=true \
ffmpeg_branding="Chrome" proprietary_codecs=true'
查看全部编译选项
gn args out/Debug --list
编译输出
ninja -C out/Debug
代码目录与分析
H264的编解码在这个目录下:
webrtc/modules/video_coding/codecs/h264
h264_decoder_impl.cc
extern "C" {
#include "third_party/ffmpeg/libavcodec/avcodec.h"
#include "third_party/ffmpeg/libavformat/avformat.h"
#include "third_party/ffmpeg/libavutil/imgutils.h"
} // extern "C"
h264_encoder_impl.cc
#include "third_party/openh264/src/codec/api/svc/codec_api.h"
#include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
#include "third_party/openh264/src/codec/api/svc/codec_def.h"
可以看到解码用了ffmpeg, 编码则是用了openh264。
细节逻辑看这两个文件 h264.cc、 h264_decoder_impl.cc
阅读代码,可以知道,代码都已经集成进去了,没生效一定是那里没设置对。
webrtc-native 增加软解支持
我找到了一下讨论组,发现已经有人提出类似的问题,大概是这样的:
https://groups.google.com/forum/#!topic/discuss-webrtc/PmVjPiewEwM
也有人贴了他的解决办法(base at M67):
enable openh264 by set 'rtc_use_h264 = true ffmpeg_branding = "Chrome" ', after this you need modify third_party/ffmpeg/chromium/config/Chrome/android/arm-neon/libavcodec/parser_list.c and third_party/ffmpeg//chromium/config/Chrome/android/arm-neon/libavcodec/codec_list.c files, add h264 parse, like:
--- a/chromium/config/Chrome/android/arm-neon/libavcodec/parser_list.c
+++ b/chromium/config/Chrome/android/arm-neon/libavcodec/parser_list.c
@@ -4,4 +4,5 @@ static const AVCodecParser * const parser_list[] = {
&ff_mpegaudio_parser,
&ff_opus_parser,
&ff_vorbis_parser,
+ &ff_h264_parser,
NULL };
--- a/chromium/config/Chrome/android/arm-neon/libavcodec/codec_list.c
+++ b/chromium/config/Chrome/android/arm-neon/libavcodec/codec_list.c
@@ -13,4 +13,5 @@ static const AVCodec * const codec_list[] = {
&ff_pcm_s32le_decoder,
&ff_pcm_u8_decoder,
&ff_libopus_decoder,
+ &ff_h264_decoder,
NULL };
但是这个解码器还不能直接用,要给上层接口,所以应该还要通过jni封装到java层。
代码上看到VP8有类似的封装,应该有一点参考价值。
寻找这个思路,找到两篇比较接近问题的博客
但是博客中有些信息比较含糊,要修改的parser_list.c
codec_list.c
config.h
的目录也错了。
webrtc M75 增加H264软解
基于这一篇博客做了修改:
https://blog.dio.wtf/post/enable-h264-codec-for-webrtc
FFmpeg 开启 H.264 软解
为 FFmpeg 开启 H.264 的编译选项,需要修改 third_party/ffmpeg 目录下的几个文件。
third_party/ffmpeg/ffmpeg_generated.gni
可以看到许多编译选项,检查下,把包含H264的开关打开。
third_party/ffmpeg/chromium/config/Chrome/{ABI}/config.h
// #define CONFIG_H264_DECODER 0
// 修改为:
#define CONFIG_H264_DECODER 1
third_party/ffmpeg/chromium/config/Chrome/android/{ABI}/libavcodec/parser_list.c
&ff_libopus_decoder,
// 增加 ff_h264_decoder
&ff_h264_decoder,
NULL };
third_party/ffmpeg/chromium/config/Chrome/android/{ABI}/libavcodec/codec_list.c
&ff_vorbis_parser,
// 增加 ff_h264_parser
&ff_h264_parser,
NULL };
- 参考上面的
编译
小节,重新编译一下。
封装JAVA层接口
- 参照 sdk/android/src/java/org/webrtc/VP8Decoder.java 和 sdk/android/src/java/org/webrtc/VP8Encoder.java 实现 H.264 的 Java 层 Wrapper
- 参照 sdk/android/jni/vp8codec.cc 实现 H.264 的 native 代码
举个例子:
#include <jni.h>
#include "modules/video_coding/codecs/h264/include/h264.h"
#include "sdk/android/generated_libvpx_h264_jni/jni/LibvpxH264Decoder_jni.h"
#include "sdk/android/generated_libvpx_h264_jni/jni/LibvpxH264Encoder_jni.h"
#include "sdk/android/src/jni/jni_helpers.h"
namespace webrtc {
namespace jni {
static jlong JNI_LibvpxH264Encoder_CreateEncoder(JNIEnv* jni) {
return jlongFromPointer(H264Encoder::Create(cricket::VideoCodec(96, "")).release());
}
static jlong JNI_LibvpxH264Decoder_CreateDecoder(JNIEnv* jni) {
return jlongFromPointer(H264Decoder::Create().release());
}
} // namespace jni
} // namespace webrtc
- sdk/android/BUILD.gn 中分别添加 generate_jni 和 rtc_static_library 任务,并添加对应的 java 和 cpp 文件
- 在 sdk/android/api/org/webrtc/SoftwareVideoDecoderFacoty.java 和 sdk/android/api/org/webrtc/SoftwareVideoEncoderFacoty.java 中分别注册 H.264 并添加创建 codec 的代码
注:WebRTC Android JNI 接口的 C 层函数定义,都是通过 Python 脚本 base/android/jni_generator/jni_generator.py 生成的,生成的代码在 out/{destination}/gen/sdk/android/generated_xxx_jni/jni 目录下。我们需要通过第3步生成 H.264 的 JNI 函数,并在第四步中引用生成的头文件。
另注:我们在将 H.264 的软编(解)码添加到工厂类里时,需要传入一个 Map 作为参数。sdk/android/src/java/org/webrtc/H264Utils.java 提供了这些生成参数的方法。这里的参数会影响协商时生成的 SDP,要根据业务实现。
使用软解
final VideoEncoderFactory encoderFactory =
new SoftwareVideoEncoderFactory();
// new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), /*enableIntelVp8Encoder*/true,/*enableH264HighProfile*/false);
final VideoDecoderFactory decoderFactory =
// new HardwareVideoDecoderFactory(eglBase.getEglBaseContext());
new SoftwareVideoDecoderFactory();
// new PlatformSoftwareVideoDecoderFactory(eglBase.getEglBaseContext());
//new DefaultVideoDecoderFactory(eglBase.getEglBaseContext());
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
final AudioDeviceModule adm = JavaAudioDeviceModule.builder(activity.getApplicationContext()
).setUseHardwareAcousticEchoCanceler(false
).setUseHardwareNoiseSuppressor(false
).setAudioRecordErrorCallback(new AudioRecordErrorCallbackAdapter()
).setAudioTrackErrorCallback(new AudioTrackErrorCallbackAdapter()
).createAudioDeviceModule();