本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。微信號:a1018998632,交流qq羣:859640274前端
不知不覺已經到了2019年,本系列的文章也更新到了8篇。很慶幸筆者能堅持下來,從我司的代碼中學習到了不少東西。固然更慶幸的是收穫了衆多讀者的鼓勵和支持。從本篇文章開始,咱們將接觸短視頻 app 中比較核心的功能——視頻編輯,筆者在我司的平常工做中,也常常對這個模塊進行開發,能夠說對這部分功能比較熟悉了。因此最近的幾篇文章,我會從零開始完善一個視頻編輯 sdk 的各類功能,最後集成到咱們以前的 MyTiktok 項目中。注:本文以 android 平臺爲例子,ios 由於不會,因此暫時不涉及。java
本文分爲如下章節,讀者可按需閱讀:python
我想看本文的人有很大一部分都是 android 工程師,因此在講乾貨以前,我須要講一講方法論android
那麼廢話很少說,就開始搭建咱們的項目吧。注意:目前 MyTiktokVideoEditor 已經上傳到了 github 上面了,建議結合項目食用,ios
上面講了如何搭建項目,這一章就來說講如何集成一些基礎庫吧。c++
首先咱們都知道,在 android 中咱們可使用 gradle 向遠程中央倉庫拉取咱們須要的庫。像 java 的 maven、js 的 npm、ios 的 pods都有這個能力。可是在 c/c++ 上的項目管理工具 CMake 就沒有這個能力,它只能在本地搜索和集成你已經安裝好的庫或者源碼,並且 c/c++ 又不具備跨平臺能力。因此最終就致使了咱們若是想使用 ffmpeg、protobuf 這樣大型的開源項目都須要本身去 clone 源碼而後本身編譯出不一樣平臺的庫。git
----代碼塊1,本文發自簡書、掘金:什麼時候夕-----
cmake_minimum_required(VERSION 3.4.1)
# 當前文件存在的目錄
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
# MyTiktokVideoEditor 的根目錄
set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../..)
# ffmpeg 的目錄
set(FFMPEG_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../android_ffmpeg)
# protobuf 頭文件與靜態庫的目錄
set(PROTOBUF_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../android_protobuf)
# android 專用 c++ 代碼的目錄
set(EDITORSDK_JNI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/editorsdk)
# c++ 共享代碼的目錄
set(SHARED_CODE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../sharedcode)
# c++ 的版本
set(CMAKE_CXX_STANDARD 11)
# 找到 android ndk 的 log 庫
find_library(log-lib log)
# 將 libffmpeg.so 添加到 libffmpeg 這個 動態 library中
add_library(libffmpeg SHARED IMPORTED)
set_target_properties(libffmpeg PROPERTIES IMPORTED_LOCATION
${FFMPEG_LIB_DIR}/armeabi/libffmpeg.so)
# 將 libprotobuf.a 添加到 libprotobuf-lite 這個 靜態 library 中
add_library(libprotobuf-lite STATIC IMPORTED)
set_target_properties(libprotobuf-lite PROPERTIES IMPORTED_LOCATION
${PROTOBUF_LIB_DIR}/armeabi/libprotobuf-lite.a)
aux_source_directory(${SOURCE_DIR} SOURCE_DIR_ROOT)
# 將全部本身寫的 c++ 代碼添加到 mttvideoeditorsdkjni 這個 動態 library 中
list(APPEND SOURCE_DIR_ROOT
${EDITORSDK_JNI_DIR}/native-lib.cc
${EDITORSDK_JNI_DIR}/ffmpeg_sample_six.cpp)
list(APPEND SOURCE_DIR_ROOT
${SHARED_CODE_DIR}/editorsdk/base/av_utils.cc
${SHARED_CODE_DIR}/editorsdk/generated_protobuf/editor_model.pb.cc)
add_library(mttvideoeditorsdkjni
SHARED
${SOURCE_DIR_ROOT})
# 將全部頭文件添加到一個列表中,在最後一塊兒連接
list(APPEND SOURCE_DIR_INCLUDE
${SHARED_CODE_DIR}/editorsdk/base/av_utils.h
${SHARED_CODE_DIR}/editorsdk/base/blocking_queue.h
${SHARED_CODE_DIR}/editorsdk/generated_protobuf/editor_model.pb.h
${PROTOBUF_LIB_DIR}/include # 將 protobuf 的頭文件放入一個列表中
${FFMPEG_LIB_DIR}/include) # 將 ffmpeg 的頭文件放入一個列表中
target_include_directories(mttvideoeditorsdkjni PRIVATE ${SOURCE_DIR_INCLUDE}) # 鏈接列表中全部的頭文件
list(APPEND LINK_LIBRARIES
mttvideoeditorsdkjni
-landroid
libprotobuf-lite
libffmpeg) # 將全部的庫添加到一個列表中,最後一塊兒連接
target_compile_options(mttvideoeditorsdkjni PUBLIC -D_LIBCPP_HAS_THREAD_SAFETY_ANNOTATIONS -Wthread-safety -Werror -Wall -Wno-documentation -Wno-shorten-64-to-32 -Wno-nullability-completeness)
target_link_libraries(${LINK_LIBRARIES} ${log-lib}) # 連接全部庫
複製代碼
----代碼塊2,本文發自簡書、掘金:什麼時候夕-----
#!/bin/bash
show_msg() {
echo -e "\033[36m$1\033[0m"
}
show_err() {
echo -e "\033[31m$1\033[0m"
}
# protobuf 的版本
v3_0_0="v3.0.0"
# 當前的目錄
script_path=$(cd `dirname $0`; pwd)
# protoc 是 protobuf 編譯以後生成的可執行文件,能夠用來根據 proto 文件生成 java、c++等等代碼
protoc_path=$script_path/tools/protoc
# protobuf 的源碼地址
protoc_src=$script_path/protobuf
# 生成的 java 文件須要移動到的位置
java_target_path="$script_path/../android/mttvideoeditorsdk/src/main"
# 生成的 c++ 文件須要移動的位置
cpp_target_path="$script_path/../sharedcode/editorsdk/generated_protobuf"
# 本方法用於執行 protobuf 源碼的腳本進行編譯
build_protobuf() {
mkdir -p $protoc_src/host
mkdir -p $protoc_path/$1
cd $protoc_src/host
../configure --prefix=$protoc_path/$1 && make -j8 && make install
if test $? != 0; then
show_err "Build protobuf failed"
exit 1
fi
cd $script_path
rm -rf $protoc_src/host
}
# 本方法用於 clone protobuf 的源碼,而後 checkout 到3.0.0的版本,而後調用 build_protobuf 進行編譯
build() {
git clone https://github.com/google/protobuf.git
show_msg "Building android protobuff source code"
cd protobuf
git checkout $v3_0_0
git cherry-pick bba446b # fix issue https://github.com/google/protobuf/issues/2063
./autogen.sh
build_protobuf $v3_0_0
show_msg "Build protobuf complete"
cd $script_path
rm -rf protobuf
}
# 若是 protoc 不存在,那麼就去 clone protobuf 的源碼,而後編譯
if [ ! -x "$protoc_path/$v3_0_0/bin/protoc" ]; then
build
fi
# 刪除以前已經生成的 java c++ 文件
rm $java_target_path/java/com/whensunset/mttvideoeditorsdk/model/protobuf/*.java
rm $cpp_target_path/*.pb.cc $cpp_target_path/*.pb.h
cd $script_path/../sharedproto
mkdir -p java cpp
# 用 protoc 生成 java c++ 文件
$protoc_path/$v3_0_0/bin/protoc *.proto --java_out=java --cpp_out=cpp
# 將生成的 java c++ 文件移動到對應的文件夾下
cp -r java $java_target_path
mkdir -p $cpp_target_path
cp cpp/* $cpp_target_path
rm -rf java cpp
複製代碼
最後一章咱們來定義一下在一個視頻編輯過程當中,須要用到的數據結構。程序員
syntax = "proto3";
package sharedcode;
option optimize_for = LITE_RUNTIME;
option java_package = "com.whensunset.mttvideoeditorsdk.model.protobuf";
// 用於保存一段時間,單位是秒
message TimeRange {
double start = 1;
double duration = 2;
uint64 id = 3;
}
// 一個多媒體文件的一個多媒體數據流的信息
message MediaStreamHolder {
// 視頻的長和寬
int32 width = 1;
int32 height = 2;
// 編解碼器的名稱
string codec_type = 3;
// 視頻的旋轉角度
int32 rotation = 4;
// 視頻像素的格式
int32 pix_format = 5;
// 視頻的色彩空間,rgb、yuv 等等
int32 color_space = 6;
// 視頻的色彩範圍
int32 color_range = 7;
// 視頻的 bit 流
int64 bit_rate = 8;
}
// 儲存一個多媒體文件的信息,減小反覆解析的性能消耗
message FileHolder {
string path = 1;
// 文件的後綴名
string format_name = 2;
int32 probe_score = 3;
// 文件中的多媒體數據流的數量
int32 num_streams = 4;
// 文件中的多媒體數據流的信息列表
repeated MediaStreamHolder streams = 5;
// 文件中多媒體信息流中最優的視頻流
int32 video_strema_index = 6;
// 文件中多媒體信息流中最優的音頻流
int32 audio_strema_index = 7;
string video_comment = 8;
}
message Color {
float red = 1;
float green = 2;
float blue = 3;
float alpha = 4;
}
// 素材的種類
enum AssetType {
ASSET_TYPE_VIDEO = 0;
ASSET_TYPE_AUDIO = 1;
}
// 表示一個視頻素材
message VideoAsset {
// 相同表示當前素材是一樣的
uint64 asset_id = 1;
string asset_path = 2;
FileHolder asset_video_file_hodler = 3;
// 當前素材被剪裁的時間區域
repeated TimeRange clipped_time_range = 4;
// 視頻的速度
double speed = 5;
// 視頻聲音大小
double volume = 6;
bool is_reversed = 7;
}
// 表示一個音頻的素材
message AudioAsset {
uint64 asset_id = 1;
string asset_path = 2;
FileHolder asset_audio_file_holder = 3;
repeated TimeRange clipped_time_range = 4;
double speed = 5;
double volume = 6;
bool is_repeat = 7;
}
// 表示一次視頻編輯的流程
message VideoWorkspace {
int64 work_space_id = 1;
repeated VideoAsset video_asset = 2;
repeated AudioAsset audio_asset = 3;
repeated TimeRange clipped_ranges = 4;
int32 workspace_output_width = 5;
int32 workspace_output_height = 6;
VideoEncoderType video_encoder_type = 7;
}
// 當前視頻編輯流程使用的編解碼方式
enum VideoEncoderType {
VIDEO_ENCODER_TYPE_FFMPEG_MJPEG = 0;
VIDEO_ENCODER_TYPE_MEDIACODEC = 1;
}
複製代碼
不知不覺又水了一篇文章^_^,最近的兩篇文章都是代碼多而文字少。不知道你們是否是喜歡這種方式呢?(感受之前廢話太多了,哈哈)你們有什麼建議或者意見但願能在評論區提出來。若是文章問題能夠指出是哪裏,方便我進行修改(手動@上篇文章中說我文章有錯別字的哥們)。最近點贊關注有點少啊,但願你們看完能隨手點個贊和關注,謝謝啦!github
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。 shell