Android音视频-Camera预览编码(OpenGL渲染预览、FFMpeg编码存储)

本章实现在上一节Android音视频-视频采集(OpenGL ES渲染)的基础上实现硬编码(MediaCodec)和软编码(FFMpeg)的功能。之前有写过一篇在应用层使用MediaCodec的硬编码,可以结合参考看一下Android音视频-视频编解码(H.264视频硬编硬解)

整体框架设计

这个的实现基于上一篇的代码基础,代码链接在文末列出,先看整体设计:


主要的调度类文件为mv_recording_preview_controller.cpp
主要控制调用逻辑方法以及调用步骤为上面的两个类的注释部分。
通过下面的方法选择是硬件编码还是软件编码方式:

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++的实现,先看一下项目的主要整体结构

  • 配置项目的CMakeLists.txt文件如下:
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
该类实现的主要逻辑如下图:


对照这个图片再去看代码可以很轻松的梳理出执行的大体逻辑
有一个关键难点的部分是Camera的数据是如何通过纹理拷贝线程过来的。

  • 初始化OpenGL ES的上下文环境,绑定到纹理拷贝线程(通过createEncoder方法传入EGLCore)
  • 拷贝线程的纹理使用共享的上下文EGLContext,它可以包含纹理对象、帧缓存对象等等
  • 根据传递的上下文创建拷贝纹理Surface,初始化新线程的copyTexSurface
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的交互逻辑和软编码一眼,这里主要梳理它的类本身的调用逻辑。如下图所示:


上层首先通过底层的纹理ID来绑定到Camera上,然后把Camera的数据传递给底层,底层在通过回掉上层的硬编码方法编码数据,底层拿到编码好的数据再存储到文件。
硬编码的代码执行起来没有任何问题,但是生成的文件在我使用的小米5 8.0系统上面却无法播放,问题还不知道在哪,以后可以再排查

总结

这个代码的学习有几个没有处理的问题,硬编码生成文件无法播放,应该是编码的时候代码逻辑出了问题或者手机我这个适配的问题,暂时没有解决这个问题,后面再处理。代码的逻辑梳理对照还是不难,这里的难点是OpenGL ES部分的代码,这个还得加强自己对OpenGL ES的知识,才能完全消化这个代码。

本文代码:
camerapreviewrecord
参考代码:
Android-CameraPreviewRecorder

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值