《#诗盗#·宇督公》:吓醒时见薛定谔,宇督万物带八哥。老子曾言福祸依,天地神通在观测。
注解
宇宙是一个整体,观测会改变宇宙。
《#诗盗#·宇督公》:吓醒时见薛定谔,宇督万物带八哥。老子曾言福祸依,天地神通在观测。
宇宙是一个整体,观测会改变宇宙。
《#诗盗#·戒股》:炒股真辛苦,买卖都是毒。清仓买买买,高潮两百五。
炒股真辛苦,就像在做毒品买卖,还是不炒了,安心买买买,反正高潮一天可以赚两百五。
之前在《Opus 编解码遇到的怪事》说过一个因为编码器不同而导致的怪事的解决过程,最近又出现一例类似情况了。
UMU 的任务是把从麦克风采集到的音频数据,直接编码成 AAC,然后用 live555 流化为 RTSP 协议,做服务端。其中涉及到一个 ADTS 头部的问题,理论上有没有 ADTS 都是可以的,各有可行的解决方案。但在阅读其他同事代码的时候,惊讶地发现,他特地把 ADTS 头给去掉了。而 UMU 调试时,发现 AVPacket 的数据里根本没有 ADTS 头,何来去掉之说?
有了上次的经验,UMU 很快推测,我们俩用的编码器可能不同。后来验证,确实如此:ffmpeg 3.1 有两个 AAC 编码器,一个内置的,名字是 aac,另一个第三方的 libfdk_aac,商业使用 non-free。(以前还有其它两个第三方的,因为质量不行,已经被移除,ffmpeg 官网上有说明)默认的编译方式只有前者,后者需要使用 non-free 参数编译,基于后期的版权问题考虑,UMU 使用的是内置的 aac。但为了调查这个问题,UMU 特地编译并使用了 libfdk_aac,发现确实有不同。
aac 编码出来的 AVPacket 是没有 ADTS 头的; libfdk_aac 则有。
aac 不需要设置 profile,因为它默认使用 LC,而 libfdk_aac 支持很多中 profile,所以需要设置一个合适的。
libfdk_aac 设置合适的 profile 字段,编码出来的 AVPacket 有 ADTS 头,VLC 可以播放,特地去掉 ADTS 头,VLC 也可以播放。
如果不设置 profile,默认是 FF_PROFILE_UNKNOWN,这时有 ADTS 头,但由于这个 ADTS 头里的 adts_buffer_fullness 不对,所以 VLC 无法播放,去掉反而可以。
一个工程同时使用了 ffmpeg 和 live555,结果一不注意就混乱了……原因如下:
1 | // ffmpeg 的 error.h 里 include 了 errno.h,有以下定义: |
很明显,live555 这么做,违背了面向对象的基本特征——封装,这种平台相关的抽象应该封装在源文件里面,而不是放在头文件。挪个位置即可。
刚刷完 OpenWRT trunk 版本,默认不支持 PPTP passthrough,表现为此路由器内网的 PC 拨号时,认证很快成功,但迟迟不能完成,最终报错误码 619。
这是因为默认不支持 GRE 协议的 NAT。
官方就有解决方案,简单地说是运行一下两条:
1 | opkg update |
立刻生效。
《#诗盗#·天鸡 Book 测漏》:白首相知友难交,一心逍遥,两袖飘飘;十里春风稣不嫖,九零太老,零零太早!
改编自霹雳角色“天迹”的诗号。
仙衣眠云碧岚袍,一襟潇洒,两袖飘飘;
玉墨舒心春酝瓢,行也逍遥,坐也逍遥。
《#诗盗#·有朋自远方来》:写诗浑似画虎卵,不用古精与酒坛。今日相逢无限饮,老婆太多七次难。
画虎卵:闽南语,吹牛侃大山。
古精:古经的通假字,古典经书。
《#诗盗#·绿艹囧》:绿草横四空,网兔分六笼。骑虎三流灵,科技万花捅。
最近 KJ 四个绿色的 +,大花园的乖乖兔们终于迎来六个办公室~
用了三流灵就是骑虎难下,虽然人人喊打,然并卵。
在云游戏和云桌面项目中,总结了几类声音采集技术,把录音做到极致。
最典型的就是麦克风,内置麦克风、外置麦克风,其实还有一种通过 LineIn 插入的其它播放器设备,比如 CD、DVD 等。
采集这种音频的方法可以只用 ffmpeg 搞定:av_find_input_format(“dshow”)…,也可以用 CoreAudio 搞定:
1 | enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, ... |
采集方式是用 CoreAudio:
1 | enumerator->(eRender, DEVICE_STATE_ACTIVE, ... |
这种方式会混音,比如说您开个 foobar 播歌,再开个 QQ 影音看电影,则会录到这两个应用程序的混音,嗯,如果 QQ 再嘀嘀嘀,也是会混进去的……
有个叫 Virtual Audio Cable 的虚拟声卡,能虚拟多张声卡,并且可以把声音转发到对应的虚拟 LineIn 设备,供应用程序采集。
比前一种更先进一些,多个播放器同时播歌,我们可以只录其中一个。
采集方法是:Hook CoreAudio。
另一个思路是:Hook 到这个应用,给它单独指定一个输出设备,其它应用不能用,否则还是混音了,然后用前面的回放录音技术录制这个独占的输出设备。您可能要说,哪有那么多输出设备?这个问题可以用前面提到的虚拟声卡解决,分分秒虚拟出 64 个是没问题的。而且用 VAC 的好处是,可以在这 64 个对应的 LineIn 通道直接录制,不需要用 CoreAudio,兼容性会更好。
一般情况下操作 AVAudioFifo/AVFrame 都是用全套 ffmpeg API,内部自己管理内存,不需要了解它们内部怎么组织内存。比如:
1 | inline int InitFrame(AVFrame *&frame, int frame_size = kTargetSamplesPerFrame) |
这里读了一个 AVFrame 出来,并不需要知道具体的内存布局,但如果要写入 FileMapping 对象里,就得知道了! 参考以下函数:
1 | int av_audio_fifo_read(AVAudioFifo *af, void **data, int nb_samples) |
和 AVFrame 定义:
1 | typedef struct AVFrame { |
以 AV_SAMPLE_FMT_S16 为例,发现 InitFrame() 里的 av_frame_get_buffer() 之后只有 linesize[0] 是非 0,即 data[0] 的分配长度,其它 7 个都是 0,即 data[1] -> data[7] 都没有分配,于是猜测就是读 data[0],长度 linesize[0],尝试把它写到 FileMapping 里,果然是对的。如果 SampleFormat 是带 P 的,就不是只有 data[0] 了,有几个 channel 就有几个 data,要相应改变。
京东联盟购买链接:
FFmpeg从入门到精通 出版时间:2018-04-01 用纸:胶版纸