本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。微信號:a1018998632,交流qq羣:859640274html
你們好,距離上次本專題發文已經有五個星期了,中間發了兩篇非本專題的文章,可能不少人都覺得我要棄坑了。可是並非這回事,主要是工做有點忙,並且我在音視頻方面其實也有許多東西須要學習和整理。那麼從本篇文章開始咱們就要進入音視頻領域進行研究學習了,Android 領域的文章會在中間整合音視頻代碼的時候進行穿插講解。其實 Android 裏面要講的東西仍是挺多的,奈什麼時候間不等人。廢話很少說,咱們進入文章。本文預計閱讀時間二十分鐘。前端
本文分爲如下章節,讀者能夠按需閱讀java
其實我在 個人技術成長之路 中已經大概講解了學習音視頻技術須要學習哪些東西,在這一節我會講些具體的東西,固然也只是一個粗淺的入門,更加深刻的知識仍是須要讀者本身去積累。android
Cmake 是組織 C/Cpp 項目的一個工具,相似咱們在 android 中使用的 gradle。咱們要寫一個大一點的工具,Cmake 這種項目管理工具是必不可少的。這一節就來入門一下 Cmake,注意下面的教程是 官方教程 的翻譯。ios
這是本章節對應的項目:cmake_learning項目c++
我由於主力機是 Mac,因此使用的 IDE 是 CLion,CLion 也是 JetBrain 全家桶的成員之一。使用了 Android Studio 或者 IDEA 的同窗能夠很方便的切換到這個 IDE 上。此外 CLion 仍是一個跨平臺的 IDE,也就是說在 Windows Linux 上面也可使用它。固然 Visual Studio 永遠是最強的 IDE(手動狗頭)。須要注意的是 CLion 是須要花錢買激活碼的,彷佛沒有免費版開始能免費試用一個月左右的時間,因此激活碼的獲取途徑你們就各顯神通吧。git
1.咱們進入項目中 one/a 的目錄發現下面有兩個文件:CMakeLists.txt 和 tutorial.cpp 裏面的代碼以下:程序員
# 一個 cmake 組織的項目最少有下面這三行代碼
cmake_minimum_required (VERSION 2.6) # 表示cmake的最小版本
project (Tutorial)# 新建一個project,這個project的名字叫Tutorial
add_executable(Tutorial tutorial.cpp) # 爲 Tutorial 這個 project 添加一個可執行的文件tutorial.cpp
# 1.cmake的語法支持大小、小寫和大小寫混合例如上面的 project 能夠寫成 PROJECT
複製代碼
//
// Created by 什麼時候夕 on 2018/10/20.
//
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main (int argc, char *argv[]) {
if (argc < 2) {
fprintf(stdout, "usage: %s number\n", argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
fprintf(stdout, "The square root of %g is %g\n", inputValue, outputValue);
return 0;
}
複製代碼
2.咱們進入項目中 one/b 的目錄發現下面有三個文件:CMakeLists.txt、tutorial.cpp、TutorialConfig.h.in 裏面的代碼以下:github
cmake_minimum_required (VERSION 2.6)
project (Tutorial_A)
# 咱們能夠在 cmake 的程序中添加鍵值對 set(KEY VALUE),下面就是一個鍵值對的設置方式。
# 若是想要在 cmake 文件中取出這個鍵值對則須要使用 ${KEY} 的方式
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
## 這裏能夠設置一個配置文件,咱們能夠在 TutorialConfig.h.in 中配置 set() 中設置的鍵值對
## PROJECT_SOURCE_DIR 表示的是源代碼的路徑
## PROJECT_BINARY_DIR 表示的是cmake build 的路徑
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# 將 cmake 的 build 目錄添加到cmake 尋找 include 文件的目錄列表中,這樣一來 cmake 就能找到前面生成的 TutorialConfig.h 配置文件
include_directories("${PROJECT_BINARY_DIR}")
add_executable(Tutorial_A tutorial.cpp)
複製代碼
// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// include 了cmake 生成配置文件
#include "TutorialConfig.h"
int main (int argc, char *argv[]) {
if (argc < 2)
{
fprintf(stdout,"%s Version %d.%d\n",
argv[0],
// 使用了 cmake 生成的配置參數
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
double outputValue = sqrt(inputValue);
fprintf(stdout,"The square root of %g is %g\n",
inputValue, outputValue);
return 0;
}
複製代碼
// 這個是配置文件,cmake 會根據他在 cmake 的 build 目錄生成一個 TutorialConfig.h 文件
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR
複製代碼
cmake_minimum_required (VERSION 2.6)
# 聲明瞭一個 library 名爲 MathFunctions,他包含一個可執行文件 mysqrt.cpp
add_library(MathFunctions mysqrt.cpp)
複製代碼
#include "MathFunctions.h"
#include <stdio.h>
// a hack square root calculation using simple operations
double mysqrt(double x) {
if (x <= 0) {
return 0;
}
double result;
double delta;
result = x;
// do ten iterations
int i;
for (i = 0; i < 10; ++i) {
if (result <= 0) {
result = 0.1;
}
delta = x - (result * result);
result = result + 0.5 * delta / result;
fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
}
return result;
}
複製代碼
//
// Created by 什麼時候夕 on 2018/11/11.
//
#ifndef PROJECT_MATHFUNCTIONS_H
#define PROJECT_MATHFUNCTIONS_H
double mysqrt(double x);
#endif //PROJECT_MATHFUNCTIONS_H
複製代碼
cmake_minimum_required (VERSION 2.6)
project (Tutorial_Mylib)
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# 添加一個是否使用咱們本身的庫的開關 USE_MYMATH,這個開關能夠在 cmake 中直接使用
option (USE_MYMATH
"Use tutorial provided math implementation" ON)
# 定義一個文件來儲存 USE_MYMATH,以便在 cpp 文件中使用
configure_file("${PROJECT_SOURCE_DIR}/Configure.h.in"
"${PROJECT_BINARY_DIR}/Configure.h")
include_directories("${PROJECT_BINARY_DIR}")
# 若是咱們把開關設置爲 ON,那麼就將 mylib 集成進編譯中,不然就不集成。
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/mylib")
add_subdirectory (mylib)
set (EXTRA_LIBS MathFunctions)
endif (USE_MYMATH)
add_executable (Tutorial_Mylib tutorial.cpp)
# 將library 與 project 進行連接,使得 project 中能夠調用 library 中的函數
target_link_libraries (Tutorial_Mylib ${EXTRA_LIBS})
複製代碼
#cmakedefine USE_MYMATH
複製代碼
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#include "Configure.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
int main (int argc, char *argv[]) {
if (argc < 2)
{
fprintf(stdout,"%s Version %d.%d\n", argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
#ifdef USE_MYMATH
// 若是開關開了,就使用我本身的庫
double outputValue = mysqrt(inputValue);
fprintf(stdout,"use my math");
#else
double outputValue = sqrt(inputValue);
fprintf(stdout,"not use my math");
#endif
fprintf(stdout,"The square root of %g is %g\n",
inputValue, outputValue);
return 0;
}
複製代碼
cmake_minimum_required (VERSION 2.6)
# 聲明瞭一個 library 名爲 MathFunctions,他包含一個可執行文件 mysqrt.cpp
add_library(MathFunctions mysqrt.cpp)
複製代碼
#include "MathFunctions.h"
#include <stdio.h>
// a hack square root calculation using simple operations
double mysqrt(double x) {
if (x <= 0) {
return 0;
}
double result;
double delta;
result = x;
// do ten iterations
int i;
for (i = 0; i < 10; ++i) {
if (result <= 0) {
result = 0.1;
}
delta = x - (result * result);
result = result + 0.5 * delta / result;
fprintf(stdout, "Computing sqrt of %g to be %g\n", x, result);
}
return result;
}
複製代碼
//
// Created by 什麼時候夕 on 2018/11/11.
//
#ifndef PROJECT_MATHFUNCTIONS_H
#define PROJECT_MATHFUNCTIONS_H
double mysqrt(double x);
#endif //PROJECT_MATHFUNCTIONS_H
複製代碼
cmake_minimum_required (VERSION 2.6)
project (Tutorial_Mylib)
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
# 添加一個是否使用咱們本身的庫的開關 USE_MYMATH,這個開關能夠在 cmake 中直接使用
option (USE_MYMATH
"Use tutorial provided math implementation" ON)
# 定義一個文件來儲存 USE_MYMATH,以便在 cpp 文件中使用
configure_file("${PROJECT_SOURCE_DIR}/Configure.h.in"
"${PROJECT_BINARY_DIR}/Configure.h")
include_directories("${PROJECT_BINARY_DIR}")
# 若是咱們把開關設置爲 ON,那麼就將 mylib 集成進編譯中,不然就不集成。
if (USE_MYMATH)
include_directories ("${PROJECT_SOURCE_DIR}/mylib")
add_subdirectory (mylib)
set (EXTRA_LIBS MathFunctions)
endif (USE_MYMATH)
add_executable (Tutorial_Mylib tutorial.cpp)
# 將library 與 project 進行連接,使得 project 中能夠調用 library 中的函數
target_link_libraries (Tutorial_Mylib ${EXTRA_LIBS})
複製代碼
#cmakedefine USE_MYMATH
複製代碼
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "TutorialConfig.h"
#include "Configure.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
int main (int argc, char *argv[]) {
if (argc < 2)
{
fprintf(stdout,"%s Version %d.%d\n", argv[0],
Tutorial_VERSION_MAJOR,
Tutorial_VERSION_MINOR);
fprintf(stdout,"Usage: %s number\n",argv[0]);
return 1;
}
double inputValue = atof(argv[1]);
#ifdef USE_MYMATH
// 若是開關開了,就使用我本身的庫
double outputValue = mysqrt(inputValue);
fprintf(stdout,"use my math");
#else
double outputValue = sqrt(inputValue);
fprintf(stdout,"not use my math");
#endif
fprintf(stdout,"The square root of %g is %g\n",
inputValue, outputValue);
return 0;
}
複製代碼
# 安裝這個庫,將庫和頭文件分別添加到 bin 和 include 文件夾中,最後移動到的地方以下
# /usr/local/bin/libMathFunctions_Install.a
# /usr/local/include/MathFunctions.h
install (TARGETS MathFunctions_Install DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
複製代碼
# TARGETS包含六種形式:ARCHIVE, LIBRARY, RUNTIME, OBJECTS, FRAMEWORK, BUNDLE。注意Mathfunction_Install安裝的是LIBRARY,Tutorial_Mylib_Install 是RUNTIME類型。
# FILE 將給定的文件複製到指定目錄。若是沒有給定權限參數,則由該表單安裝的文件默認爲OWNER_WRITE、OWNER_READ、GROUP_READ和WORLD_READ。
# TARGETS和FILE可指定爲相對目錄和絕對目錄。
# DESTINATION在這裏是一個相對路徑,取默認值。在unix系統中指向 /usr/local 在windows上c:/Program Files/${PROJECT_NAME}。
# 也能夠經過設置CMAKE_INSTALL_PREFIX這個變量來設置安裝的路徑,那麼安裝位置不指向/usr/local,而指向你所指定的目錄。
# 安裝這個可執行文件,將可執行文件和頭文件分別添加到 bin 和 include 文件夾中,最後移動到的地方以下
# /usr/local/bin/Tutorial_Mylib_Install
# /usr/local/include/TutorialConfig.h
install (TARGETS Tutorial_Mylib_Install DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include)
複製代碼
project(MakeTable)
add_executable(MakeTable MakeTable.cpp)
# 1.輸出 Table 文件
# 2.將 Table 文件做爲參數傳入 MakeTable 項目中,並運行它
# 3.Table 的生成是依賴於 MakeTable 這個 project 的
# CMAKE_CURRENT_BINARY_DIR 表示某個 cmake 文件build以後的文件夾,好比這裏就是指 build/mylib
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 將生成的表一塊兒編譯到 MathFunctions_Table 中去
add_library(MathFunctions_Table mysqrt.cpp ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
複製代碼
//
// Created by 什麼時候夕 on 2018/10/20.
//
#include <stdio.h>
#include <stdlib.h>
#include "math.h"
int main (int argc, char *argv[]) {
double result;
if (argc < 2) {
return 1;
}
FILE *fout = fopen(argv[1], "w");
if (!fout) {
return 1;
}
fprintf(fout, "double sqrtTable[] = {\n");
for (int j = 0; j < 10; ++j) {
result = sqrt(static_cast<double>(j));
fprintf(fout, "%g,\n", result);
}
fprintf(fout, "0};\n");
fclose(fout);
return 0;
}
複製代碼
#cmakedefine USE_MYMATH
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
複製代碼
先上一個項目:FFmpeg-learing,之後關於 FFmpeg 的 demo 都會添加到這個項目中去,你們看博客的時候仍是須要結合這個項目一塊兒看。算法
**咱們先來看第一個官方文檔中的 Demo:從視頻文件中讀取視頻信息。 **
struct buffer_data {
uint8_t *ptr;
size_t size; ///< size left in the buffer
};
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
{
struct buffer_data *bd = (struct buffer_data *)opaque;
buf_size = FFMIN(buf_size, bd->size);
if (!buf_size)
return AVERROR_EOF;
printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
/* copy internal buffer data to buf */
memcpy(buf, bd->ptr, buf_size);
bd->ptr += buf_size;
bd->size -= buf_size;
return buf_size;
}
int av_io_reading(int argc, char *argv[])
{
syslog_init();
AVFormatContext *fmt_ctx = NULL;
AVIOContext *avio_ctx = NULL;
uint8_t *buffer = NULL, *avio_ctx_buffer = NULL;
size_t buffer_size, avio_ctx_buffer_size = 4096;
char *input_filename = NULL;
char *output_filename = NULL;
int ret = 0;
struct buffer_data bd = { 0 };
if (argc != 2) {
fprintf(stderr, "usage: %s input_file\n"
"API example program to show how to read from a custom buffer "
"accessed through AVIOContext.\n", argv[0]);
return 1;
}
input_filename = argv[0];
output_filename = argv[1];
// 將 input_filename 指向的文件數據讀取出來,而後用 buffer 指針指向他,buffer_size 中存有 buffer 內存的大小
ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
if (ret < 0)
goto end;
bd.ptr = buffer;
bd.size = buffer_size;
if (!(fmt_ctx = avformat_alloc_context())) {
ret = AVERROR(ENOMEM);
goto end;
}
// 申請四個字節大小的緩衝區,在後面做爲內存對齊的標準使用
avio_ctx_buffer = (uint8_t *) av_malloc(avio_ctx_buffer_size);
if (!avio_ctx_buffer) {
ret = AVERROR(ENOMEM);
goto end;
}
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
0, &bd, &read_packet, NULL, NULL);
if (!avio_ctx) {
ret = AVERROR(ENOMEM);
goto end;
}
fmt_ctx->pb = avio_ctx;
ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open input\n");
goto end;
}
ret = avformat_find_stream_info(fmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Could not find stream information\n");
goto end;
}
av_dump_format(fmt_ctx, 0, output_filename , 0);
end:
avformat_close_input(&fmt_ctx);
/* note: the internal buffer could have changed, and be != avio_ctx_buffer */
if (avio_ctx) {
av_freep(&avio_ctx->buffer);
av_freep(&avio_ctx);
}
av_file_unmap(buffer, buffer_size);
char buf2[500] = {0};
av_strerror(ret, buf2, 1024);
if (ret < 0) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}
複製代碼
原本講兩個官方 Demo 的,可是篇幅有限就到此爲止吧。我在項目中其實已經集成了編碼視頻和解碼視頻的 demo。各個方法的定義處也有中文解釋,有興趣的同窗能夠自行查看。還要說的一件事情是,由於時間有限,其實項目裏的不少東西是不能保證運行成功的,這個問題我後面若是都測試經過了會在 commit 裏面聲明。
音視頻開篇總算寫完了,有個「偉人」說得好:你知道的越多,你不知道的就越多——什麼時候夕。我最近也感受到了本身的許多不足之處,天天早晨騎車上班的時候都會反思一下前一天作的很差的地方。吾日三省吾身,這句話無論在什麼年代都不過期啊,共勉!!!
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。