一、下载mp4v2源码
下载地址:https://code.google.com/archive/p/mp4v2/downloads下载后解压,我们要在Android平台上使用,所以需要使用NDK编译,我们需要以下源码
mp4v2编译需要源码二、源码放入Android studio工程目录
1.以下是我的目录结构
jni目录结构 mp4v2作为一个单独的共享模块 libyuv使用已经编译好的库注意: 1
mp4v2源码里有一些文件已“.in”作为后缀,要把“.in”去掉保留应有的后缀名,不然有些文件会找不到,比如mp4v2-2.0.0\libplatform\config.h.in
要改成 config.h
注意: 2
头文件的导入,源码里 mp4v2-2.0.0\include\mp4v2\mp4v2.h
,我把mp4v2.h
放到上一级的目录了,注意修改mp4v2.h
里头文件的引用,还有一下其它文件对mp4v2.h
的include,比如src.h
的头文件里对mp4v2.h
的include,因为目录有变,记得修改
三、编写cmake脚本或者Makefile脚本
如果要和工程一起build可以使用cmake脚本,编写CMakeLists.txt
文件,也可以使用makefile.我的目录结构是把mp4v2
作为一个单独的共享模块编译的,所以这里的脚本编写分为两级,第一级jni目录下,编写我们自己的jni接口源码的CMakeLists.txt
和mp4v2
作为单独模块编译的CMakeLists.txt
。而且我把mp4v2
也放在它自己的jni目录下,在它自己的模块下编写脚本
1.以下为mp4v2
模块的CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
SET ( ly_cmake_dir ${CMAKE_CURRENT_SOURCE_DIR} ) #cmake所在的目录
SET ( ly_base_dir ${PROJECT_SOURCE_DIR}/libs/mp4v2/jni )
SET ( ly_src_dir ${ly_base_dir}/src)
SET ( ly_inc_dir ${ly_base_dir}/include )
SET ( ly_lib_name mp4v2 )
SET ( ly_lib_static ${ly_lib_name} )
SET ( ly_lib_shared ${ly_lib_name}_shared )
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -fexceptions -Wno-write-strings -O2 -DHAVE_SOCKLEN_T -DHAVE_STRUCT_IOVEC")
message("------------------------------Cmake build ${ly_lib_name}-----------------------------")
message("project source dir==>${PROJECT_SOURCE_DIR}")
message("Cmake file dir==>${CMAKE_CURRENT_SOURCE_DIR}")
#查找目录下所有的源文件,并保存在指定变量ly_source_files中,遗憾的是它不能像File递归查找
aux_source_directory( src ly_source_files)
aux_source_directory( src/bmff ly_source_files)
aux_source_directory( src/itmf ly_source_files)
aux_source_directory( src/qtff ly_source_files)
aux_source_directory( libplatform ly_source_files)
aux_source_directory( libplatform/io ly_source_files)
aux_source_directory( libplatform/number ly_source_files)
aux_source_directory( libplatform/process ly_source_files)
aux_source_directory( libplatform/prog ly_source_files)
aux_source_directory( libplatform/sys ly_source_files)
aux_source_directory( libplatform/time ly_source_files)
aux_source_directory( libutil ly_source_files)
# FILE ( GLOB_RECURSE ly_source_files ./src/*.cpp )
#LIST ( SORT ly_source_files )
#排序之后的文件
message("-----------------The following is the sorted source file--------------------")
foreach(_var ${ly_source_files})
message("${_var}")
if(_var MATCHES "_win32.cpp")
list(APPEND filter_source_cpp ${_var})
endif()
endforeach()
foreach(_var ${filter_source_cpp})
message("remove win32==>${_var}")
# 切记此处remove_item 的list不能 ${} 求值
list(REMOVE_ITEM ly_source_files ${_var})
endforeach()
message("---------------------------------------------------------------------------")
if (${ANDROID_ABI} STREQUAL "armeabi-v7a")
message("------------------------${ly_lib_name}::armeabi-v7a-----------------------------")
set_property(SOURCE ${ly_source_files} APPEND_STRING PROPERTY COMPILE_FLAGS " -mfpu=neon")
endif ()
# include目录 AFTER OR BEFORE 表示添加在之前还是之后,SYSTEM表示系统包含目录
# https://cmake.org/cmake/help/latest/command/include_directories.html
include_directories( BEFORE ${ly_inc_dir} ${ly_base_dir}
${ly_cmake_dir}/src ${ly_cmake_dir}/libutil ${ly_cmake_dir}/libplatform)
# this creates the static library (.a)
ADD_LIBRARY ( ${ly_lib_static} STATIC ${ly_source_files} )
#-------------------------------------------------------------------------------------------
# this creates the shared library (.so)
#ADD_LIBRARY ( ${ly_lib_shared} SHARED ${ly_source_files} )
#可以输出同名的 动、静态库
#SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES OUTPUT_NAME "${ly_lib_name}" )
## SET_TARGET_PROPERTIES ( ${ly_lib_shared} PROPERTIES PREFIX "lib" )
#cmake在构建完一个target构建下一个target时会clean掉上一个使用此target名字的库
#set_target_properties ( ${ly_lib_static} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#set_target_properties ( ${ly_lib_shared} PROPERTIES CLEAN_DIRECT_OUTPUT 1)
#动态库版本号
##set_target_properties ( ${ly_lib_shared} properties VERSION 1.0 SOVERSION 1)
2.以下为外层我们JNI接口的CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
#输出更详细的构建信息
# SET( CMAKE_VERBOSE_MAKEFILE on )
SET ( ly_cmake_dir ${CMAKE_CURRENT_SOURCE_DIR} ) #cmake所在的目录
SET ( ly_base_dir ${PROJECT_SOURCE_DIR} )
SET ( ly_src_dir ${ly_base_dir}/src )
SET ( ly_inc_dir ${ly_base_dir}/include )
SET ( ly_lib_name mp4 )
SET ( ly_lib_static ${ly_lib_name} )
SET ( ly_lib_shared ${ly_lib_name}_shared )
SET ( MY_LOG_TAG yuv-tag)
message(STATUS "Source_Dir==>${PROJECT_SOURCE_DIR}")
message(STATUS "Cmake_file_Dir==>${CMAKE_CURRENT_SOURCE_DIR}")
message(STATUS "CMAKE_CURRENT_LIST_FILE==>${CMAKE_CURRENT_LIST_FILE}")
message(STATUS "Android NDK::${ANDROID_NDK}")
message(STATUS "ENV Android NDK::$ENV{NDK_ROOT}")
message(STATUS "ANDROID_ABI==>${ANDROID_ABI}")
message(STATUS "ANDROID_PLATFORM==>${ANDROID_PLATFORM}")
message(STATUS "CMAKE_ANDROID_ARCH_ABI==>${CMAKE_ANDROID_ARCH_ABI}")
#-std=c++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pipe -fPIC -Wall -frtti -fexceptions")
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
#------------------------------------------------------------------------------------------
#add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
message("-------------------------compile subdirectory ${ly_lib_name}------------------------------")
add_subdirectory(${ly_cmake_dir}/libs/mp4v2/jni ${ly_cmake_dir}/libs/mp4v2)
message("---------------------------compile ${ly_lib_name} finished--------------------------------")
set(sub_lib_name mp4v2)
#------------------------------------------------------------------------------------------
message("--------------------------Cmake build ${ly_lib_name}----------------------------")
FILE ( GLOB_RECURSE yuv_source_files ${ly_src_dir}/*.cpp ) #递归搜索目录下所有的c文件
LIST ( SORT yuv_source_files )
message("---------------------The following is the sorted source file---------------------")
foreach(_var ${yuv_source_files})
message("${_var}")
endforeach()
message("---------------------------------------------------------------------------------")
#------------------------------------------------------------------------------------------
#追加编译参数 add_definitions("-DMY_LOG_TAG=${MY_LOG_TAG}")
#add_compile_options(-DMY_LOG_TAG=${MY_LOG_TAG})
if(NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug")
message("---------------------${ly_lib_name}::Compile the release version---------------------")
# add_compile_options( -DMY_LOG_LEVEL=MY_LOG_LEVEL_ERROR )
else()
message("---------------------${ly_lib_name}::Compile the Debug version-----------------------")
# add_compile_options( -DMY_LOG_LEVEL=MY_LOG_LEVEL_VERBOSE )
endif()
#------------------------------------------------------------------------------------------
include_directories( AFTER ${ly_inc_dir} ${ly_base_dir}/libs/libyuv/include ${ly_base_dir}/libs/mp4v2/jni/include)
#查找一系列平台已提供的原始库,find_library(变量名 库名称)
find_library(log-lib log)
#find_library(graphics-lib jnigraphics)
find_library(android-lib android)
# 导入其它预构建库,使用 IMPORTED
# add_library( imported-lib SHARED IMPORTED )
# set_target_properties( imported-lib PROPERTIES IMPORTED_LOCATION imported-lib/src/${ANDROID_ABI}/libimported-lib.so)
# include_directories( imported-lib/include/ )
add_library( yuv-static STATIC IMPORTED )
set_target_properties( yuv-static PROPERTIES IMPORTED_LOCATION ${ly_base_dir}/libs/libyuv/libs/${ANDROID_ABI}/libyuv.a)
# 链接库使用的language
#set_target_properties(${ly_lib_shared} PROPERTIES LINKER_LANGUAGE CXX)
add_library( ${ly_lib_name} SHARED ${yuv_source_files} )
target_link_libraries(${ly_lib_name} yuv-static mp4v2 ${log-lib} ${android-lib})
#设置本次target的构建依赖其它target,确保本target构建前,其它target先被构建好
add_dependencies(${ly_lib_name} ${sub_lib_name})
三、在build.gradle
里配置cmake CMakeLists.txt
1.build.gradle
里的脚本配置
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode sdk_version_code
versionName sdk_version_name
ndk {
// abiFilters('x86', 'armeabi', 'armeabi-v7a', 'arm64-v8a')
abiFilters('armeabi-v7a')
}
externalNativeBuild {
cmake {
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang","-DANDROID_STL=gnustl_static"
}
}
}
externalNativeBuild {
cmake {
path 'src/main/jni/CMakeLists.txt'
}
// ndkBuild {
// path 'src/main/jni/Android.mk'
// }
}
至此我们就可以跟随工程编译,编译出我们需要的库,最终编译的release版本已经strip去掉符号表的库在build\intermediates\transforms\stripDebugSymbol\release\folders\2000\3\main\lib\\
未去除符号表的库在目录下build\intermediates\transforms\mergeJniLibs\release\folders\2000\3\main\lib\\
目录下
四、mp4v2使用需要注意的知识点
至于mp4v2的接口调用网上已经太多了,就不介绍接口的使用,按照网上的基本没错,然后最终的就是要知道一下几点知识
- audioTrack和videoTrack都创建添加到MP4之后才可以写流数据
- 必须线程安全的通过
MP4WriteSample
写数据,就是不能多线程同时写数据,否则写出了的MP4文件播放中间自动停止播放错误的情况,elecard
都无法解析MP4 中的h264 - h264的start code “
0x00 00 00 01
”,需要改写成h264除去0x00000001
四个字节外的有效数据附载长度的大端字节形式 -
AAC
音频数据不能加ADTS header
- Android使用
MediaCodec
编码的第一帧音频或者视频数据都是codec config,通过判断编码数据的(bufferinfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0
知道是配置数据,视频可以取出SPS、PPS
给MP4AddH264VideoTrack
使用,音频2个字节的数据给MP4SetTrackESConfiguration
使用 - 编码数据的
bufferinfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME
说明是关键帧,写视频数据MP4WriteSample(...,isSyncSample)
最后一个参数通过上面的判断,关键帧isSyncSample
就等于1,非关键帧就等于0.
以下为一些参考
https://code.google.com/archive/p/mp4v2/downloads
http://blog.csdn.net/baiseled/article/details/52622269
http://blog.csdn.net/baiseled/article/details/52643223
markdown语法
markdown使用语法
网友评论