FFmpeg 基本使用

最近接手公司一个视频相关项目, 也是使用ffmpeg工具. 需要快速了解下. 概念 码率, 帧率, 文件大小 帧率(frame rate) : 视频一秒中有多少帧画面(frames per second, fps), 决定视频流畅度. interlaced: 古代黑白相机的交错扫描呈现, 描述单位i : 50i progressive: 现代整页整页呈现, 描述单位p: 60p 码率(bit rate) :视频一秒中有多少位, 决定视频的质量 ABR: 平均码率 CBR: 常量码率 VBR: 动态码率 文件大小 = 视频文件 + 音频文件 视频文件 = 码率 * 时间(s) / 8 音频文件 = 码率 * 时间(s) / 8 文件格式 不同的视频文件类型, 用户存储特定的数据流( 在 ffmpeg 中称为 containers) 可以存储声音或者视频数据. 相关缩写 encoding (E) decoding (D) video (V) audio (A) subtitles (S) 文件元数据 metadata 描述媒体文件自身的信息, 比如: ...

<span title='2022-08-19 00:14:54 +0800 +0800'>August 19, 2022</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;潜水员

Redis 用于做分布式锁

操作 演进 加锁后进程挂掉了 加锁成功之后, 进程挂掉了没有进行解锁操作. 导致进入死锁状态. 引入 expire 设置超时时长, 自动释放 key 1 2 3 4 5 6 7 8 > setnx lock:key true > OK > expire lock:key 5 " ... do something critical ... > del lock:codehole 加锁动作 到 expire 之间挂掉了 redis 2.8 之后支持 set 拓展指令 1 > set lock:key true ex 5 nx 加锁互斥锁, 并同时设置超时时长 执行超时, 被其他进程获取到了锁 加锁之后, 本身进程执行时间超过了预先设置的 expire 的时间. 就会导致锁被提前释放. 解决方案: ...

<span title='2022-08-18 12:18:53 +0800 +0800'>August 18, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

CI/CD

CI 自动化发布流程 可重复 可快速回滚 发布流程快速 在合并的时候可以对代码进行快速验证 CD 部署流程平台化 一键部署 总结 所谓的 CI/CD 就是通过自动化操作, 比如自动化测试, 自动化部署. 使用项目拥有快速上线的能力 – 可以快速验证, 快速回滚, 快速发布.

<span title='2022-08-14 15:27:33 +0800 +0800'>August 14, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Golang Map 介绍

本想写一篇关于 golang 中 map 底层的文章, 但是发现已经了相当不错的文章 – 字节跳动技术团队 - Golang 中 map 探究 这里只补充一下,缺少的 map 的删除操作 内部数据结构 初始化 map 是一个有"包含内容"的数据结构, 使用之前需要提前初始化, 即调用make 真正是调用源码是 runtime.makemap 获取数据 删除 源码地址 删除的关键代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // Only clear key if there are pointers in it. // # 当 Key 是指针类型的时候会去清空指针 if t.key.ptrdata != 0 { if goarch.PtrSize == 8 { *(*unsafe.Pointer)(k) = nil } else { // There are three ways to squeeze at one ore more 32 bit pointers into 64 bits. // Just call memclrHasPointers instead of trying to handle all cases here. memclrHasPointers(k, 8) } } e := add(unsafe.Pointer(b), dataOffset+bucketCnt*8+i*uintptr(t.elemsize)) // # 当 Value 为指针类型的时候, 指针为空, 解除引用 -> GC if t.elem.ptrdata != 0 { memclrHasPointers(e, t.elem.size) } else { memclrNoHeapPointers(e, t.elem.size) } // # 讲 hash 值标记为空 b.tophash[i] = emptyOne 上述删除代码操作现象 ...

<span title='2022-08-13 14:14:30 +0800 +0800'>August 13, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Mysql- 数据类型 - 数字

概览 Mysql 支持以下数据类型 数字类型 串类型(字符和字节) 日期和时间 空间 JSON 数字类型 整数类型 Interger 类型 存储大小 bytes 其他别名 TinyInt 1 bool, boolean = tinyint(1) SmallInt 2 MediumInt 3 Int 4 BigInt 8 int(M) 表示显示宽度, 最大显示宽度为(255), M 与存储空间的大小无关. 空间大小由具体类型决定. 如果具体数值达不到宽度, 左边就会用0值补齐至 M 位. 浮点 ( Floating-Point ) 类型 存储 补充 范围 Float 4 bytes 单精度 Double 8 bytes 双精度 Float(p) p 表示小数点后的精度位数 Float(M, D) Mysql 语法: M表示总显示位数, D表示小数点后个数 – 由Mysql自己做约分处理. Mysql 8.0 后废弃该语法. ...

<span title='2022-07-31 17:49:46 +0800 +0800'>July 31, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Mysql- 数据类型 - 日期

概览 Mysql 支持以下数据类型 数字类型 串类型(字符和字节) 日期类型 空间 JSON 日期类型 Mysql 支持的数据类型 DATE TIME DATETIME TIMESTAMP YEAR Date 只存储日期数据, 不包含时间. YYYY-MM-DD, 范围是从 ‘1000-01-01’ to ‘9999-12-31’ DateTime 存储日期, 也存储时间 'YYYY-MM-DD hh:mm:ss' 范围是从'1000-01-01 00:00:00’ to ‘9999-12-31 23:59:59’ TIMESTAMP 存储Unix时间戳数据 会受到服务器时区影响– 存储的时候转化成标准的Unix时间戳(0时区), 取数据时反之 时区环境变量设置 范围 ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC. 相关函数: FROM_UNIXTIME 把 Unix 时间戳转化成日期 UNIX_TIMESTAMP 把日期转化成 Unix 时间戳 YEAR 显示形式 YYYY, 可选显示位数YYYY(M) 默认4位显示 ‘1991’ TIME 只有时间部分,没有日期部分 hh:mm:ss, 范围从 ‘-838:59:59’ 到 ‘838:59:59’ ...

<span title='2022-07-31 17:49:46 +0800 +0800'>July 31, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;潜水员

xorm 的 session 和 salve 的区别

简单分析下xorm 里面 session 和 slave 里面 close 的代码 1 xorm.EngineGroup.Slave() Slave () 会直接返回 xorm.Engine对象(指代我们项目到数据库的逻辑连接(里面有tcp复用)) 如果这时候再调用 close 的会,会直接把 xorm.Engine 关闭(即数据库连接关闭)。 如果用这个 engine, 执行查询动作如 find, get时. 内部会有自动执行 NewSsession 和 Close, 所以不在需要手动调用close 1 xorm.EngineGroup.NewSsession() 会返回一个xorm.Session对象, 对应我们数据库操作中的"会话事务", 是否自动提交之类选项.(未手动开始事务时,都是自动提交) 调用 close, 对清除 session 中未提交的事务和一些缓存的 sql 前置处理语句 对于普通 select 语句调不调用是没什么太大影响 (但是还是建议new之后调用close)

<span title='2022-07-22 19:08:05 +0800 +0800'>July 22, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Make 的基本使用

想自己整理一篇基本的 make 指令用法, 突然发现 阮一峰大佬已经整理了一篇很完整的博客, 遂放弃. 阮一峰文章地址 如果不写 c, 主要理解就几个概念就可以使用了 target 可以用来当作想要执行的命令集的名称 .PHONY: 可以用来声明命令集名称 recipes 实际执行的命令集合 介绍一下我自己的应用场景 我目前主力编程语言是 go, 我用的编辑器是 vim, 所以我基本就在 shell 里面完成编码任务. case 1: 简化本地编译和测试, 自动做 setup 和 teardown 当我想要尝试一下整个项目是否编译 1 2 3 4 .PHONY: build build: go build . rm -rf [PROJECT NAME] 使用上面的 makefile, 我就只需要 make build, 就不用再删除编译出来文件. QAQ, 可以再加一些单元测试命令, 检测测试是否通过. 因为公司的项目, 在单测这方面做的不是很好, 我自己就是简单 build 一下 case 2: git 提交代码自动化操作 当我想要把我代码推送到, 测试分支, 进行集成测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .PHONY: dev ProjectName="Your Project Name" TargetBranch="Your want to merge branch" CurBranch=$(shell git branch --show-current) dev: go build . rm -f $(ProjectName) git add . git commit -m $(msg) git push git checkout ${DevBranch} git pull --rebase git merge ${CurBranch} -m "Merge branch '${CurBranch}' into ${DevBranch}" go build rm -f $(ProjectName) git push git checkout ${CurBranch} 简化 git 的操作流程, 现在只需要make dev就可以完成, 还可以在合并之前和之后增加测试, 我自己目前知识简单的 build 下而已 QAQ. ...

<span title='2022-07-10 17:47:42 +0800 +0800'>July 10, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

RabbitMQ 基本入门实践

公司刚好有一个项目要使用到 RabbitMQ, 本文是我自己了解学习 RabbitMQ 的记录. 介绍一下 RabbitMQ Erlang 语言实现 AMQP (Advanced Message Queuing Protocal) 的消息中间件 消息中间件的作用 解耦: 可以不需要依赖下游的可用性, 提高单独项目的可用性. 冗余存储: 保存失败的消息 拓展性 削峰 缓冲 结构 涉及的名词简单解释 Producer Consumer Broker: 服务节点 Queue: 内存存储消息数据的对象 Exchange: 选择器, 生产者投递消息后, 提交给交换器, 由交换器根据routingkey 和 bindingkey 决定投递到哪个队列 RoutingKey: 生产消息提供routingkey 给交换器用于指定要投递的队列 BindingKey: 交换器, 通过 bindingkey 与对应的队列关联起来 Connection: 客户端与 Broke 建立的 TCP 连接 Channel: 是建立在 Connection 上抽象的虚拟连接. 实现多线程可以 TCP 连接, 多个信道可能复用同一个 TCP 连接 交换器的类型 fanout : 投递到所有队列 direct: 投递到 routingkey 和 bindingkey 匹配的 topic: direct 的拓展, 支持模糊匹配 headers : 性能差, 少用 消息投递流程: ...

<span title='2022-07-09 16:43:26 +0800 +0800'>July 9, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;潜水员

什么是 "胶水代码"

胶水代码(glue code), 可以了解为是大号的适配器, 没有业务意义, 主要的目的是用于 连接 两个 不兼容 的组件, 或是两个不兼容的服务. 胶水代码可以是一段代码, 也可以是一个脚本文件, 甚至可以是新的组件. 参考 Quora 回答 wiki

<span title='2022-06-18 20:04:38 +0800 +0800'>June 18, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Go中slice[i:j:k]第三个参数是做什么的

今天, 突然被同事卷到了, 被问到 golang 中 slice 的三个参数是干嘛的? 我突然一时间忘记了, golang 的重切片居然是可以接受第三个参数的, 枉费我已经了写了快两年的 go 了. 赶紧 google 一下, 并总结备忘. 简单介绍 slice 的数据结构 首先, 介绍一下 golang 中切片的结构体: 1 2 3 4 5 type slice struct { array unsafe.Pointer len int cap int } 有三个字段: array 是切片所指向的底层数组数据 len 就是切片的长度 cap 即容量, 很明显 源码地址 简单版重切片 a[low:high] 接受切片中的开始下标和结束下标, “左闭右开原则” 即重的切片数据会包含 low 下标的值, 但没有 high 下标的值 新切片的 容量(cap) 即为开始下标到原 slice 数据容量结束, 即"cap(a) - low" low 参数可以省略, 默认从 0 下标开始 high 参数也可以省略, 默认就是 slice 的长度, 即 len 的 value 1 2 3 4 5 6 7 8 a := [10]int{} oldSlice := a[:5] newSlice := a[2:4] fmt.Printf("b: len %d, cap %d, c: len %d, c:cap %d", len(b), cap(b), len(c), cap(c)) 输出: b: len 5, cap 10, c: len 2, c:cap 8 底层分配情况如下: ...

<span title='2022-06-09 23:52:30 +0800 +0800'>June 9, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;潜水员

设计模式 -- 策略模式

场景 在有多种算法, 计算策略, 甚至业务流程以一种可相互替换的场景出现时. 就可以考虑该设计模式. 比如: 地图导航的出行路线计算, 商品购买的捆绑促销方式等. 分析 优点: 将具体的策略与调用策略的流程代码解耦, 修改策略代码时候可以仅修改策略对象 在不同策略之间划清界限 增加单独策略的可测试性 缺点: 后续变更有一定束缚性 ( 限制在抽象接口中 ) . 若需要打破策略的抽象接口, 就会造成较大的改动 会带来的问题: 可能将原先一个流程代码, 拆分成了多分. 如果代码结构没有组织好, 想要大局把握可能比较费力 总结: 在流程算法策略有多个 ( 如果只有一个就没有必要抽象了, 一梭子写完就行 ), 且各个策略都有一定复杂性 ( 只是简单几个 if 的也略了吧 ) 的场景下. 就可以使用 ( 接口的签名可以写的比较松一点, 传递和返回较高层级的数据对象 ). 实现流程 主要的思想, 我觉得还是 面向接口编程 分析要拆分的算法或者策略, 将主流程和策略部分, 提取抽象成一个通用接口(interface) 将算法或策略剥离成独立的策略对象, 并实现上述抽象接口 主流程代码根据业务逻辑, 初始化策略对象, 将需要的策略"插入"(plugin)流程, 调用, 获取结果, ok. 参考 refactoringguru - strategy

<span title='2022-06-08 19:50:42 +0800 +0800'>June 8, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

计算机中的:大端和小端

大端和小端的命名出处是来自于《格列佛游记》中吃鸡蛋分成从大端开始吃的“大端派”和从小端开始吃的“小端派” 大端的优势是高位计算,和可读性 小端的优势的低位运算 各自的优劣分析本质上还是因为内存的连续性,需要修改和读取的位数越少越有优势。 以上是阅读阮一峰的博文-字节序探析:大端与小端的比较的简单总结。本来自己也是想要整理一篇关于大小端分析的文章。刚好阮老师发文了,收获甚多,就不在自己整理了。 参考连接 阮一峰的博文-字节序探析:大端与小端的比较

<span title='2022-06-04 00:19:23 +0800 +0800'>June 4, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

小窥 Go 中的 Modules

是什么 Modules (下文称为模块)是 Go 语言设计用来管理依赖包。 在中文中我们经常把go中的 Module 和 Package 统称为“包”,其实 Module 是一个版本控制的“包”的集合。 模块的引入主要用于解决,项目依赖的不同版本的问题。(在旧的 GOPATH 中不同项目依赖不同版本)。引入的模块会声明在go.mod文件中。 go.mod 文件 1 2 3 4 5 6 7 8 9 module example.com/my/thing go 1.12 require example.com/other/thing v1.0.2 require example.com/new/thing/v2 v2.3.4 exclude example.com/old/thing v1.2.3 replace example.com/bad/thing v1.4.5 => example.com/good/thing v1.4.5 retract [v1.9.0, v1.9.5] 文件中各行意思 module 当前自己模块的名的完整路径 go 版本号 require 需要依赖的目标模块以及最小的版本号 exclude 排除目标模块的特定版本 replace 将某个模块版本指向另一个模块版本,也可以指向本地,未发布的模块 retract 当前发布的模块,需要撤回不能用版本,用于处理意外发布版本的情况 go.mod 可以通过相关命令修改,也可以直接修改文件数据 go.sum 文件 与go.mod经常在一起的会是一个go.sum文件,文件中存放着第一次添加依赖模块时,的模块源码的 hash 值。主要是用于校验,防止依赖模块代码被意外地修改到。 ...

<span title='2022-06-03 18:16:55 +0800 +0800'>June 3, 2022</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;潜水员

Mysql 不支持事务嵌套

mysql 在事务中再开启事务,前一个事务会被自动提交 stackoverflow

<span title='2022-06-02 16:16:58 +0800 +0800'>June 2, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

并发与并行的区别

举个例子,电脑的鼠标,键盘或者其他设备的驱动程序,他们是并发的,但不是并行的。他们也不需要并行去运行。 并发是很多程序(形容运行任务,不是广义上的程序)的独立运行,并发是一种程序设计结构 并行是一次性运行很多程序,并行是一种程序的运行现象,当成程序设计成并发的,他很容易就变成了并行, 但并行不一定是并发的目的。 Concurrency is not Parallelism by Rob Pike

<span title='2022-05-31 22:36:34 +0800 +0800'>May 31, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Go-互斥锁的实现

Mutex 数据结构 1 2 3 4 type Mutex struct { state int32 sema uint32 } Mutex 使用过之后是不可被拷贝的 state 等于 0 值的时候才是无锁的状态 sema 字段为信号量字段,通过该字段控制协程的阻塞和唤醒,具体实现在runtime 中。 Mutex 对象总共有三个公开方法 Lock 尝试抢占互斥锁,如果已经被锁定,则调用协程进入阻塞 TryLock Unlock 解除互斥锁, 解锁未锁定的互斥锁会发生panic Mutex 与协程无法关,允许一个协程锁定,另一个协程进行解锁。 Mutex 实现了一个 sync.Locker 接口, 该接口只有两个方法 Lock Unlock Mutex 锁有几种状态 mutexLocked = 1 已经锁定 mutexWoken = 2 表示当前锁的等待队列,有协程正在活跃地获取锁,可以考虑不用释放信号量 mutexStarving = 4 当前锁已经进入了饥饿状态 其他常量 mutexWaiterShift = 3 统计的等待在Mutex.state字段等待数量。(前3位,用于表示锁的状态, 即 mutexLocked, mutexWoken, mutexStarving) starvationThresholdNs = 1e6 进入饥饿模式的阈值 1ms Mutex 锁的竞争方式 Mutex 锁有两种状态 ...

<span title='2022-05-29 20:52:35 +0800 +0800'>May 29, 2022</span>&nbsp;·&nbsp;4 min&nbsp;·&nbsp;潜水员

【翻译】使用 Godoc 给 go 代码添加文档

在看官方文档的时候,突然心血来潮,想翻译一下一篇博客玩玩。 原文章地址 Godoc: documenting Go code Go 语言官方对于代码的文档非常重视,文档对于软件的可阅读性和可维护性有着至关重要的地位。当然,文档必须要是准确可理解的,也需要易于编写和维护。理性情况下,文档应该与代码紧密联系。这样子才能方便于程序员修改和编写。 所以,我们开发了 godoc 文档工具。本文描述了的 godoc 关于的“文档”的方法论,和解释如何按照使用规范给你自己的项目写出一个好的文档。 godoc 会解析源文件代码和注释,生成基于HTML的页面或者纯文本格式的文档。这样子文档就与代码紧密联系。举个例子,通过在 godoc 的页面点击,你就可以在一个函数的文档说明和源代码中快速跳转。 godoc 在概念上与 Python 的“Docstring”和 Java 的“Javadoc"相似,但是 godoc 设计的更加简单化。godoc 读取的代码注释的不需要特定的结构化(Docstring 使用),也不需要特定的语法(JavaDoc 使用),godoc 从代码读取的注释就是简单的“代码注释”,就算你不使用 godoc 也可以直接阅读的注释。 使用的规范很简单:在类型,变量,常量,函数,包声明的上方写下注释即可, 中间不要有空行。godoc 会把这些注释以文字的形式呈现在被注释的对象旁边。举个例子,下方就是 fmt 包的Fprint函数的注释。 1 2 3 4 // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 值得注意的是该注释是以被注释的对象命名开头的一个完整的句子。 这个使用规范可以方便我们生成各种各样的格式文档,从简单的纯文本到 UNIX 是 man 的帮助页,还可以使用其他工具更见简单地获取到信息, 比如提取出第一行或者句子。 ...

<span title='2022-05-25 16:11:10 +0800 +0800'>May 25, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

IP表示中 / 代表的意思

IP 中斜杠表示的是子网掩码的位数 例如 202.118.130.0/24 表示 子网页码的位数有24位,即 1111.1111.1111.0000

<span title='2022-05-23 22:01:03 +0800 +0800'>May 23, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员

Hugo 如何使用已经发布文章做为相对URL

hugo 的默认内容都是在 content/ 路径下方 在 markdown 文章中使用 { {< ref "/blog/my-first-post.md" >}}(两个左花括号中间是没有空格的), 会在编译时发生地址替换, 带/ 是表示从 content/ 目录下的下一个绝对路径 1 [我的文章]({ {< ref "/blog/my-first-post.md" }}) 记得是已经发布的文章, 如果使用不存在, 或者是不参加发布的草稿文章就会发生报错 hugo官方文档

<span title='2022-05-15 16:33:13 +0800 +0800'>May 15, 2022</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;潜水员