从零开始仿写一个抖音App——音视频开篇

  • 3.我们可以在 two/a/build 中运行 cmake…、make、./Tutorial_Mylib 3 这几个命令,会发现最终调用的是我们自己的函数,如果将 USE_MYMATH 改成 OFF 然后删除 build 中的文件再重新 build 一遍,会发现最后调用的是系统的函数。

cmake_minimum_required (VERSION 2.6)
project (Tutorial_Mylib)

set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)

configure_file (
P R O J E C T S O U R C E D I R / T u t o r i a l C o n f i g . h . i n " " {PROJECT_SOURCE_DIR}/TutorialConfig.h.in" " PROJECTSOURCEDIR/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(“ P R O J E C T S O U R C E D I R / C o n f i g u r e . h . i n " " {PROJECT_SOURCE_DIR}/Configure.h.in" " PROJECTSOURCEDIR/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;
}

(2).添加库的依赖
  • 1.我们进入项目的 two/a/mylib 中会看见三个文件 CMakeLists.txt、mysqrt.cpp、MathFunctions.h 代码如下:
  • 1.声明了一个 library
  • 2.定义了一个计算平方根的函数,然后使用头文件暴露在外面

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

  • 2.然后我们再看看 two/a 这个目录下面的文件,这些文件大部分是从 one/b 中拷贝来的,我就只贴有修改的部分 CMakeLists.txt、Configure.h.in、MathFunctions.h、tutorial.cpp:
  • 1.这里主要做的工作是现在 cmake 文件中定义了一个 USE_MYMATH 的开关,当这个开关为 ON 的时候就将我们定义的 library 集成到 project 中,否则就不集成,只使用系统自带的库。这个东西在跨平台的时候非常有用,比如 ios 和 android 中的 log 库不同,那么我就可以定义一个开关来区别这两个平台。
  • 2.可以注意到的是这里也定义了一个 Configure.h.in 文件作为配置文件,cmake 会根据这个文件来创建一个 Configure.h 文件,然后我们就可以在 Cpp 文件中使用我们定义的开关了。
  • 3.我们可以在 two/a/build 中运行 cmake…、make、./Tutorial_Mylib 3 这几个命令,会发现最终调用的是我们自己的函数,如果将 USE_MYMATH 改成 OFF 然后删除 build 中的文件再重新 build 一遍,会发现最后调用的是系统的函数。

cmake_minimum_required (VERSION 2.6)
project (Tutorial_Mylib)

set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)

configure_file (
P R O J E C T S O U R C E D I R / T u t o r i a l C o n f i g . h . i n " " {PROJECT_SOURCE_DIR}/TutorialConfig.h.in" " PROJECTSOURCEDIR/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(“ P R O J E C T S O U R C E D I R / C o n f i g u r e . h . i n " " {PROJECT_SOURCE_DIR}/Configure.h.in" " PROJECTSOURCEDIR/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;
}

(3).安装库与可执行文件
  • 1.我们进入项目的 three/a 文件夹中,这里面的文件都是从 two/a 中复制过来的,我只将增加的代码列一下mylib/CMakeLists.txt、a/CMakeLists.txt:
  • 1.这里就比较简单了,就只是将我们生成的库与可执行文件安装到电脑中去
  • 2.先依次运行cmake …、make、make install,然后可以运行 /usr/local/bin/Tutorial_Mylib_Install 3 来查看是否安装成功,注意这里的路径是 Mac 电脑的路径。

安装这个库,将库和头文件分别添加到 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)

(4).Cmake生成Cpp文件
  • 1.我们进入 four/a 目录中,这里的代码都是从 two/a 中拷贝过来的,所以我就只贴修改的部分,mylib/CMakeLists.txt、mylib/MakeTable.cpp、a/Configure.h.in:
  • 1.这里的目的主要是通过 MakeTable 这个 project 生成一个 Table.h。最后给 mysqrt.cpp 在当前系统中没有 log 和 exp 这两个函数的时候使用。
  • 2.我们运行了 cmake… 之后会发现 build/mylib 目录中生成了 Table.h 这个文件

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(j));
fprintf(fout, “%g,\n”, result);
}
fprintf(fout, “0};\n”);
fclose(fout);
return 0;
}

#cmakedefine USE_MYMATH
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

(5).CMake语法
  • 1.必填、[command]可填、a|b 均可
  • 2.cmake 可以三种形式组织文件
  • 3.文件夹形式:类似 gradle 根目录下需要有一个 CMakeList.txt 的文件作为入口,如果其他目录下面还 需要有新的子文件夹要编译,子文件夹下也需要有 CMakeList.txt。而且需要在根目录 CMakeList.txt 下 用 add_subdirectory() 来注明。此外,每个CMakeList.txt 在被处理的时候都是以 cmake 命令调用 的文件夹作为当前工作目录和输出目录。
  • 4.定义和取消变量用的是 set() 和 unset(),被定义的变量始终是字符串类型,变量名区分大小写。 变量名用外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传{${}}
  • 5.add_excutable() 和 add_library() 分别用于生成可执行文件与库。构建 android so 库的时候 可使用 add_library()。target_link_libraries() 用于链接n个互相之间有依赖关系的库
  • 6.message([] “message to display”) 这个方法用于输出日志
(6).CMake流程语句
  • 1.if:用法类似c语言,在使用参数的时候不需要用${}来取值
  • 2.foreach:foreach(loop_var 1 2 3) … endforeach(loop_var) 或者 foreach(loop_var RANGE 4) … endforeach(loop_var) 或者 foreach(loop_var RANGE 0 3 1) … endforeach(loop_var) 从 0 到 3,1是步伐
  • 3.while:while(condition) … endwhile(condition)
  • 4.foreach 和 while 可用 break 和 continue,在循环中使用${}进行取值
  • 5.可用 option 和 if 进行配合。option(<option_var> “description” [initial_var])
(7).宏与方法
  • 1.macro( [a1 [a2 [a3 …]) … endmacro(),可在内部使用 ${a1} 来引用变量,
  • 1.ARGV#,#是下标,可用于引用变量
  • 2.ARGV,表示所有传入变量
  • 3.ARGN,传入了需要参数以外的参数
  • 4.ARGC,传入的参数总个数
  • 5.macro 是字符替换,类似 c 语言中的预处理,所以在 if 中使用的时候需要 ${} 来获取参数
  • 2.function( [a1 [a2 [a3 …]) … endmacro(),与 macro 类似,但是不是字符替换, 是实实在在的调用函数。

四、FFmpeg官方demo讲解

先上一个项目:FFmpeg-learing,以后关于 FFmpeg 的 demo 都会添加到这个项目中去,大家看博客的时候还是需要结合这个项目一起看。

1.项目结构

  • 1.首先先了解一下这个项目的结构吧,如图1:
  • 1.图1的 java 目录下面我想大家应该都清楚,放的是开发 android 的 java 文件
  • 2.然后我们看 jni/ffmpeg 这个目录中有三个文件夹:
  • 1.armeabi:这里放的是 so 文件,这里的 so 文件是我从 ffmpeg 的源码中编译过来的。每一个 so 文件都对应着我们在第二章中讲解的一个 FFmpeg 的模块代码。
  • 2.include:这里放的是 FFmepg 各个模块暴露出来的 .h 文件,也就是说我们需要通过 .h 文件中的函数定义来调起各个 so 文件中的函数实现。
  • 3.my:这里放的是我写的代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 2.再来看看项目中的 Cmake 文件,因为 android studio 目前支持 Cmake 文件来管理 android 中的 Cpp代码。
  • 1.如果第三章你认真看过了的话,那么这里应该也很好理解。这里主要新增了两个我们之前没有讲到的 cmake 命令:
  • 1.find_library:这个命令主要是用来寻找本地存在的库的路径的,在这里我去寻找 log 这个库在本地的路径然后将其赋值给 log-lib 这个参数。使用 message 输出一个 ${log-lib} 我们可以发现其就是 android ndk 目录下面的 liblog.so 文件,其主要用于 android 的日志输出。除此之外,你可以使用这个命令去寻找你在本地拥有的各种so文件。
  • 2.set_target_properties:这个命令主要是将各种 so 文件的路径转化成简单的值。 例如:set_target_properties( postproc-54 PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jni/ffmpeg/armeabi/libpostproc-54.so) 这里就是将 so 文件的地址转化成 postproc-54 这个简短的名字以便后面使用。
  • 3.剩下来的代码我就简单说了:主要就是将一个个 so 文件声明成一个个 library,最后使用target_link_libraries 命令将我写的代码与各个 so 文件的 library 再链接起来,这样最终就能将所有的 Cpp 代码打包到 android app 中去。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.FFmpeg读取视频文件信息

**我们先来看第一个官方文档中的 Demo:从视频文件中读取视频信息。 **

  • 1.首先根据我们在第二章节描述的多媒体概念我们可以知道:视频的数据有许多的封装格式,比如 MP4、avi、flv 等等。在 FFmpeg 中用于处理这些视频格式的 struct (因为 FFmpeg 使用 c 写的所以,内部还没有类的概念。)就是 AVFormatContext。大家可以进入这个 struct 可以看见其定义其实和 java 中的 class 类似。有成员变量,有函数指针(用于代替成员函数)。
  • 2.有了解析视频数据封装格式的 struct,我们还需要一个能从文件中读取数据的东西。在 FFmpeg 中这个东西就是 AVIOContext,这个东西是 AVFormatContext 的成员变量,用于从不断的从文件中读取数据,然后将数据送给 AVFormatContext 解析。
  • 3.科普了两个 struct 我们就可以讲解 demo 了。入口是下面代码中的 av_io_reading 方法,这个方法的入参是 argc 表示 argv 数组的数量,argv 中有两个参数 分别表示输入文件与输出文件。注意:接下来我在文章中讲解的 FFmpeg 的方法,已经下载过项目的同学可以直接去方法定义的地方查看,我讲过的方法的文档我都翻译成便于理解的中文了。
  • 1.首先在前面定义了一堆变量
  • 1.比如我们前面说的两个 struct。
  • 2.然后是定义了两个 unit8_t 的指针,其实 unit8_t 就是 unsigned char 大家可以进入看看它的定义。而熟悉 c 的同学应该知道,unsigned char 指针其实一般指向的就是一块内存类似于 java 中的 byte 数组。
  • 3.然后定义了两个 size_t 分别表示2中定义的两个 unit8_t 指针指向的内存大小。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

[外链图片转存中…(img-FO61lq76-1710684471405)]

详细整理在GitHub可以见;

Android架构视频+BAT面试专题PDF+学习笔记

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

由于文字描述有限,这里给出一个简单的HTML和CSS代码示例,它创建了一个基础的登录界面,你可以根据这个例子去调整和添加更多风格的元素: ```html <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="styles.css"> <title>风格登录页</title> </head> <body> <div class="login-container"> <img src="tiktok-logo.png" alt="Logo" class="logo"> <form id="login-form"> <input type="text" placeholder="请输入用户名" id="username" required> <input type="password" placeholder="请输入密码" id="password" required> <button type="submit">登录</button> <a href="#" class="social-login">已有账号?立即登录</a> </form> <div class="animation"> <!-- 可以添加动态加载效果的代码,如旋转的波浪线 --> <span class="wave"></span> </div> </div> </body> </html> ``` ```css /* styles.css */ .login-container { width: 300px; margin: auto; padding: 20px; background-color: #f0f0f0; border-radius: 5px; } .logo { display: block; margin-bottom: 15px; max-width: 100%; } input[type=text], input[type=password] { width: 100%; padding: 10px; margin-bottom: 15px; font-size: 16px; border: none; border-bottom: 1px solid #ccc; } button { background-color: #ff6e51; color: white; border: none; padding: 10px 20px; cursor: pointer; transition: background-color 0.3s ease; } .social-login { text-decoration: underline; color: #4c99d4; } .animation .wave { animation: wave 1s linear infinite; } @keyframes wave { 0%, 100% {transform: rotate(0deg);} 50% {transform: rotate(-20deg);} } ``` 这只是一个基本框架,为了完全复制风格,还需要进一步调整颜色、字体、图标和动画效果,甚至添加响应式设计。实际项目中可能还需要结合JavaScript来处理表单验证和提交事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值