本章实现在上一节Android音视频-视频采集(OpenGL ES渲染)的基础上实现硬编码(MediaCodec)和软编码(FFMpeg)的功能。之前有写过一篇在应用层使用MediaCodec的硬编码,可以结合参考看一下Android音视频-视频编解码(H.264视频硬编硬解)
这个的实现基于上一篇的代码基础,代码链接在文末列出,先看整体设计:
void MVRecordingPreviewController::startEncoding(const char* h264FilePath, int width, int height, int videoBitRate, float frameRate, bool useHardWareEncoding) {
if(NULL != encoder){
delete encoder;
encoder = NULL;
}
if (useHardWareEncoding){
encoder = new HWEncoderAdapter(g_jvm, obj);
} else {
encoder = new SoftEncoderAdapter();
}
encoder->init(h264FilePath, width, height, videoBitRate, frameRate);
if (handler)
handler->postMessage(new Message(MSG_START_RECORDING));
}
上层调用的时候传递想要的编码方式即可。
底层使用C++的实现,先看一下项目的主要整体结构
cmake_minimum_required(VERSION 3.4.1)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
set(PATH_TO_MEDIA_CORE ${CMAKE_SOURCE_DIR}/src/main/cpp)
set(PATH_TO_THIRD_PARTY ${PATH_TO_MEDIA_CORE}/3rdparty/ffmpeg/include/)
set(PATH_TO_PRE_BUILT ${PATH_TO_MEDIA_CORE}/3rdparty/prebuilt/${ANDROID_ABI})
include_directories(${PATH_TO_THIRD_PARTY})
include_directories(${PATH_TO_MEDIA_CORE}/)
include_directories(${PATH_TO_MEDIA_CORE}/common/)
include_directories(${PATH_TO_MEDIA_CORE}/camera_preview/)
include_directories(${PATH_TO_MEDIA_CORE}/video_encoder/)
file(GLOB FILES_CAMERA_PREVIEW "${PATH_TO_MEDIA_CORE}/camera_preview/*.cpp")
file(GLOB FILES_COMMON "${PATH_TO_MEDIA_CORE}/common/*.cpp")
file(GLOB FILES_COMMON_EGL_CORE "${PATH_TO_MEDIA_CORE}/common/egl_core/*.cpp")
file(GLOB FILES_COMMON_MSG_Q "${PATH_TO_MEDIA_CORE}/common/message_queue/*.cpp")
file(GLOB FILES_COMMON_GL_MEDIA "${PATH_TO_MEDIA_CORE}/common/opengl_media/*.cpp")
file(GLOB FILES_COMMON_GL_MEDIA_RENDER "${PATH_TO_MEDIA_CORE}/common/opengl_media/render/*.cpp")
file(GLOB FILES_COMMON_GL_MEDIA_TEXTURE "${PATH_TO_MEDIA_CORE}/common/opengl_media/texture/*.cpp")
file(GLOB FILES_COMMON_GL_MEDIA_TEXTURE_COPIER "${PATH_TO_MEDIA_CORE}/common/opengl_media/texture_copier/*.cpp")
file(GLOB FILES_COMMON_SL_MEDIA "${PATH_TO_MEDIA_CORE}/common/opensl_media/*.cpp")
file(GLOB FILES_VIDEO_ENCODER "${PATH_TO_MEDIA_CORE}/video_encoder/*.cpp")
file(GLOB FILES_VIDEO_ENCODER_HW "${PATH_TO_MEDIA_CORE}/video_encoder/hw_encoder/*.cpp")
file(GLOB FILES_VIDEO_ENCODER_SOFT "${PATH_TO_MEDIA_CORE}/video_encoder/soft_encoder/*.cpp")
file(GLOB FILES_VIDEO_ENCODER_SOFT_COLOR "${PATH_TO_MEDIA_CORE}/video_encoder/soft_encoder/color_conversion/*.cpp")
add_library( # Sets the name of the library.
camerapreview
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${FILES_THIRD_PARTY}
${FILES_CAMERA_PREVIEW}
${FILES_COMMON}
${FILES_COMMON_EGL_CORE}
${FILES_COMMON_MSG_Q}
${FILES_COMMON_GL_MEDIA}
${FILES_COMMON_GL_MEDIA_RENDER}
${FILES_COMMON_GL_MEDIA_TEXTURE}
${FILES_COMMON_GL_MEDIA_TEXTURE_COPIER}
${FILES_COMMON_SL_MEDIA}
${FILES_VIDEO_ENCODER}
${FILES_VIDEO_ENCODER_HW}
${FILES_VIDEO_ENCODER_SOFT}
${FILES_VIDEO_ENCODER_SOFT_COLOR}
${PATH_TO_MEDIA_CORE}/LPreviewScheduler.cpp
)
# Include libraries needed for renderer lib
target_link_libraries(
camerapreview
# 引入系统的动态库
log
android
GLESv2
EGL
z
OpenSLES
# 引入ffmpeg相关静态库
${PATH_TO_PRE_BUILT}/libavfilter.a
${PATH_TO_PRE_BUILT}/libavformat.a
${PATH_TO_PRE_BUILT}/libavcodec.a
${PATH_TO_PRE_BUILT}/libpostproc.a
${PATH_TO_PRE_BUILT}/libswresample.a
${PATH_TO_PRE_BUILT}/libswscale.a
${PATH_TO_PRE_BUILT}/libavutil.a
${PATH_TO_PRE_BUILT}/libpostproc.a
${PATH_TO_PRE_BUILT}/libfdk-aac.a
${PATH_TO_PRE_BUILT}/libvo-aacenc.a
${PATH_TO_PRE_BUILT}/libx264.a
)
软编码的主要实现类文件为soft_encoder_adapter.cpp
该类实现的主要逻辑如下图:
bool SoftEncoderAdapter::initialize() {
pixelSize = videoWidth * videoHeight * PIXEL_BYTE_SIZE;
hostGPUCopier = new HostGPUCopier();
eglCore = new EGLCore();
eglCore->init(loadTextureContext);
copyTexSurface = eglCore->createOffscreenSurface(videoWidth, videoHeight);
eglCore->makeCurrent(copyTexSurface);
renderer = new VideoGLSurfaceRender();
renderer->init(videoWidth, videoHeight);
glGenFramebuffers(1, &mFBO);
//初始化outputTexId
glGenTextures(1, &outputTexId);
glBindTexture(GL_TEXTURE_2D, outputTexId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, videoWidth, videoHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
return true;
}
后面的关键部分就是OpenGL ES的操作了,现在暂时不仔细研究那部分的代码了,以后再补充
硬编码在这里的实现主要类文件为hw_encoder_adapter.cpp在底层和类MVRecordingPreviewController的交互逻辑和软编码一眼,这里主要梳理它的类本身的调用逻辑。如下图所示:
这个代码的学习有几个没有处理的问题,硬编码生成文件无法播放,应该是编码的时候代码逻辑出了问题或者手机我这个适配的问题,暂时没有解决这个问题,后面再处理。代码的逻辑梳理对照还是不难,这里的难点是OpenGL ES部分的代码,这个还得加强自己对OpenGL ES的知识,才能完全消化这个代码。
本文代码:
camerapreviewrecord
参考代码:
Android-CameraPreviewRecorder