1.发现摄像头设备
# On linux:
vimer@host:~/pic$ v4l2-ctl --list-devices
HD Camera: HD Camera (usb-0000:00:14.0-10):
/dev/video0
/dev/video1
# On windows:
ffmpeg -list_devices true -f dshow -i dummy
[dshow @ 0000014762dda980] DirectShow video devices (some may be both video and audio devices)
[dshow @ 0000014762dda980] "Integrated Camera"
[dshow @ 0000014762dda980] Alternative name "@device_pnp_\\?\usb#vid_5986&pid_02d2&mi_00#7&7f65834&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 0000014762dda980] "HD Camera"
[dshow @ 0000014762dda980] Alternative name "@device_pnp_\\?\usb#vid_0bc8&pid_5880&mi_00#6&19d0255b&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 0000014762dda980] DirectShow audio devices
[dshow @ 0000014762dda980] "麦克风阵列 (2- Realtek High Definition Audio)"
[dshow @ 0000014762dda980] Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{DB187E2D-D643-4E74-9C68-1FF045E372DC}"
查看摄像头的直播:
~$: cheese (On linux)
ffplay -f dshow -i video="HD Camera" (On Windows)
可以直接使用”HD Camera”.
vimer@host:~/pic$ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
Index : 0
Type : Video Capture
Pixel Format: 'MJPG' (compressed)
Name : Motion-JPEG
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 800x600
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1600x1200
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 1920x1080
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 2048x1536
Interval: Discrete 0.067s (15.000 fps)
Size: Discrete 2592x1944
Interval: Discrete 0.067s (15.000 fps)
Size: Discrete 3264x2448
Interval: Discrete 0.067s (15.000 fps)
Index : 1
Type : Video Capture
Pixel Format: 'YUYV'
Name : YUYV 4:2:2
Size: Discrete 640x480
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 800x600
Interval: Discrete 0.050s (20.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.100s (10.000 fps)
Size: Discrete 1600x1200
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 1920x1080
Interval: Discrete 0.200s (5.000 fps)
Size: Discrete 2048x1536
Interval: Discrete 0.500s (2.000 fps)
Size: Discrete 2592x1944
Interval: Discrete 0.500s (2.000 fps)
Size: Discrete 3264x2448
Interval: Discrete 0.500s (2.000 fps)
vimer@host:~/pic/test$ v4l2-ctl --device /dev/video1 --stream-mmap --stream-to=frame.raw --stream-count=30
以mmap方式获取raw frame,stream-to=frame.raw 表明保存的文件为frame.raw 后面的参数指明了帧数(可以指明摄像头的默认帧数)。如果安装 ImageMagick 的话,可以使用下面的命令把raw转换为image。
convert -size 640x480 -depth 16 uyvy:frame10.raw frame10.png
源代码工具可以看这里
在linux和windows使用ffmpeg请参考这个 link
Constant Rate Factor (crf)能决定在转码时的压缩效果,值越高,压缩越高,视频质量就越差。 在 264 和 265 中可以使用 –crf [0-51]. For x264, sane values are between 18 and 28. The default is 23. ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4 For x265, the default CRF is 28:
ffmpeg -i input.mp4 -c:v libx265 -crf 28 output.mp4
使用ffmpeg录制未压缩的视频(然后进行转码):
ffmpeg -f v4l2 -i /dev/video1 -codec:v copy rawvideo.nut (On linux)
其中,rawvideo.nut大小为260416KB.
ffmpeg -f dshow -i video="HD Camera" -codec:v copy raw_win.nut(On windows)
使用下面的命令将raw文件转换为其他格式。
ffmpeg -i rawvideo.nut -codec:v libx264 -crf 23 -preset medium -pix_fmt yuv420p -movflags +faststart output.mp4
output.mp4大小为 154KB
可以参考这个文件 https://www.ensta-bretagne.fr/zerr/dokuwiki/doku.php?id=gstreamer:main-gstreamer
gst-launch-1.0 -v filesrc location=raw_win20s.nut ! jpegdec ! videoconvert ! autovideosink
(命令释义: 把raw_win20s.nut(jpeg压缩格式)文件以 jpegdec 方式进行解码 ,然后让videoconvert进行自动转码,然后存放于autovideosink) gst以管道的形式传递参数。其中, 例如jpegdec什么的,可以看成元素。至于查看某个元素的具体信息可以使用
gst-inspect-1.0 | grep jpegdec
去查看。 当然, gst-inspect-1.0的信息更全面。
2. gst-launch-1.0 -v v4l2src ! autovideosink (以v4l2为源文件,然后发送至autovideosink)
如果找不到摄像头的合适格式、参数,可以使用使用上面的命令看下log输出,然后填入 caps,测试下面的命令是可以的:
gst-launch-1.0 -v v4l2src device=/dev/video0 ! "video/x-raw, width=(int)3264, height=(int)2448, framerate=(fraction)2/1, format=(string)YUY2, pixel-aspect-ratio=(fraction)1/1, colorimetry=(string)2:6:5:1, interlace-mode=(string)progressive" ! autovideosink
800*600
源文件 编码、大小(du -h) 转码格式-[OS] 大小-时长 命令 评价
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx265 [pc] 368KB-21s ffmpeg -i raw_linux_dai.nut -codec:v libx265 -crf 23 -preset medium -pix_fmt yuv420p -movflags +faststart h265.mp4
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx265 [pc] 224KB-21s ffmpeg -i raw_linux_dai.nut -codec:v libx265 -crf 28 -preset medium -pix_fmt yuv420p -movflags +faststart h265.mp4 最好
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx265 [pc] 188KB-21s ffmpeg -i raw_linux_dai.nut -codec:v libx265 -crf 30 -preset medium -pix_fmt yuv420p -movflags +faststart h265.mp4
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx264 [pc] 912KB ffmpeg -i raw_linux_dai.nut -codec:v libx264 -crf 20 -preset medium -pix_fmt yuv420p -movflags +faststart h264.mp4
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx264 [pc] 568KB ffmpeg -i raw_linux_dai.nut -codec:v libx264 -crf 23 -preset medium -pix_fmt yuv420p -movflags +faststart h264.mp4 最好
raw_linux_dai.nut (rawvideo : YUY2) 344MB libx264 [pc] 284KB ffmpeg -i raw_linux_dai.nut -codec:v libx264 -crf 28 -preset medium -pix_fmt yuv420p -movflags +faststart h264.mp4
gst-launch-1.0 -v filesrc location=edu111-orig.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc control-rate=0 bitrate=1000000 peak-bitrate=6500000 iframeinterval=100 ratecontrol-enable=0 quant-i-frames=30 quant-p-frames=30 quant-b-frames=30 preset-level=4 MeasureEncoderLatency=1 profile=0 ! h265parse ! qtmux ! filesink location=buka-nvv4l2decoder-nvv4l2h265enc.mp4
【推荐这个参数列表】 ```bash gst-launch-1.0 -v filesrc location=edu111-orig.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc ! h265parse ! qtmux ! filesink location=buka-nvv4l2decoder-nvv4l2h265enc-no-args.mp4 (这个命令是可以简化的最简形式,转码后的文件相当大) gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h264enc control-rate=0 bitrate=1000000 peak-bitrate=6500000 iframeinterval=100 ratecontrol-enable=0 quant-i-frames=30 quant-p-frames=30 quant-b-frames=30 preset-level=4 MeasureEncoderLatency=1 profile=0 ! h264parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2h264enc.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc control-rate=0 bitrate=1000000 peak-bitrate=6500000 iframeinterval=100 ratecontrol-enable=0 quant-i-frames=30 quant-p-frames=30 quant-b-frames=30 preset-level=4 MeasureEncoderLatency=1 profile=0 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2h265enc.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc iframeinterval=100 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-i-frame-100.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc iframeinterval=30 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-i-frame-30.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc control-rate=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-vbr.mp4 (Control-rate-1:VBR, 0:disable, 2:CBR, 3:Variable bit rate with frame skip. The encoder skips frames as necessary to meet the target bit rate.,4:Constant bit rate with frame skip) (Control-rate-1:VBR, 0:disable, 2:CBR, 3:Variable bit rate with frame skip. The encoder skips frames as necessary to meet the target bit rate.,4:Constant bit rate with frame skip) gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc control-rate=1 bitrate=6000000 peak-bitrate=6500000 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-cr3.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc qp-range="10,30:10,35:10,35" ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-qp-range.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc preset-level=0 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-present-level.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc preset-level=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-present-level.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc profile=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-profile_1.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc insert-sps-pps=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-insert-sps-pps.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc EnableTwopassCBR=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-twopasscbr.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc slice-header-spacing=8 bit-packetization=0 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-slice-header.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc slice-header-spacing=1400 bit-packetization=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-slice-header-1400.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc insert-sps-pps=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-insert-sps-pps.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc EnableTwopassCBR=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-nvv4l2-twopasscbr.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc slice-header-spacing=8 bit-packetization=0 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-slice-header.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc slice-header-spacing=1400 bit-packetization=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-slice-header-1400.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc insert-aud=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-insert-aud.mp4 gst-launch-1.0 -v filesrc location=edu10-03.mp4 ! qtdemux name=demux demux.video_0 ! queue ! h264parse ! nvv4l2decoder ! nvv4l2h265enc insert-vui=1 ! h265parse ! qtmux ! filesink location=test-nvv4l2decoder-insert-vui.mp4 ```
文件 大小 原格式 - 分辨率 转码格式 转码后后大小 转码时间 命令 评价
edu111-orig.mp4 1956KB h264 h265 1056KB 0.836s 1
edu111-orig.mp4 1956KB h264 h265 4180KB 0.700s 2
edu10-03.mp4 184MB h264 2K h264 2K 58MB 0:06:30 3 文字有失真
edu10-03.mp4 184MB h264 2k h265 2k 53MB 0:12:13 4 文字有失真
edu10-03.mp4 184MB h264 2k h265 2k 243MB 0:02:21 5 卡顿
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:02:21 6 卡顿(I帧影响运行)
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:02:21 7
edu10-03.mp4 184MB h264 2k h265 2k 373MB 0:02:01 8
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:58 9
edu10-03.mp4 184MB h264 2k h265 2k 244MB 0:02:09 10
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:58 11
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:58 12
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:57 13
edu10-03.mp4 184MB h264 2k h265 2k 328MB 0:03:33 14
edu10-03.mp4 184MB h264 2k h265 2k 266MB 0:02:37 15 有闪屏
edu10-03.mp4 184MB h264 2k h265 2k 252MB 0:01:57 16
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:57 13
edu10-03.mp4 184MB h264 2k h265 2k 328MB 0:03:33 14
edu10-03.mp4 184MB h264 2k h265 2k 266MB 0:02:37 15 有闪屏
edu10-03.mp4 184MB h264 2k h265 2k 252MB 0:01:57 16
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:55 21
edu10-03.mp4 184MB h264 2k h265 2k 247MB 0:01:55 22
Nvidia 硬件编码参数及含义():
名称 参考命令含义 参考命令
I-Frame Interval 全帧中I帧的间距,不宜太大。对运动画面影响较大,数值越小,文件越大。
temporal-tradeoff 人为强制丢弃帧,以5帧内为单位。Not Supported
control-rate 0: disable,1: variable bit rate,2: CBR, 3:Variable bit rate with frame skip, 4: Constant bit rate with frame skip
peak bitrate
指定bitrate的一个范围,默认的最大值是平均值的1.2倍。
Set Quantization Range for I, P and B Frame qp-range="10,30:10,35:10,35": [0:51] for I, P和B帧
Set Hardware Preset Level
0:UltraFastPreset,1:FastPreset,2:MediumPreset,3:SlowPreset
值越大,体积越大
Set Profile 1: Baseline profile, 2:Main profile(Not Used)(Not Used), 8: High profile(Not used)(Not used)
num-B-Frames
254enc not supported
insert-sps-pps a sequence parameter set (SPS) and a picture parameter set (PPS) are inserted before each IDR frame in the H.264/H.265 stream(具体作用还未清晰)
Two-Pass CBR Two-pass CBR must be enabled along with constant bit rate (control-rate=2)
slice-header-spacing=8 bit-packetization=0 The parameter bit-packetization=0 configures the network abstraction layer (NAL) packet as macroblock (MB)-based, and slice-header-spacing=8 configures each NAL packet as 8 MB at maximum. 15
slice-header-spacing=1400 bit-packetization=1
16
num-B-Frames
254enc not supported
insert-sps-pps a sequence parameter set (SPS) and a picture parameter set (PPS) are inserted before each IDR frame in the H.264/H.265 stream(具体作用还未清晰)
Two-Pass CBR Two-pass CBR must be enabled along with constant bit rate (control-rate=2)
slice-header-spacing=8 bit-packetization=0 The parameter bit-packetization=0 configures the network abstraction layer (NAL) packet as macroblock (MB)-based, and slice-header-spacing=8 configures each NAL packet as 8 MB at maximum. 15
slice-header-spacing=1400 bit-packetization=1 The parameter bit-packetization=1 configures the network abstraction layer (NAL) packet as size-based, and slice-header-spacing=1400 configures each NAL packet as 1400 bytes at maximum. 16
cabac-entropy-coding=1 H265 Not Supported
EnableMVBufferMeta This property enables motion vector metadata for encoding (long long time)
insert-aud This property inserts an H.264/H.265 Access Unit Delimiter (AUD) 21
insert-vui Usability Information (VUI) in SPS 22
poc-type
0: POC explicitly specified in each slice header (the default)
2: Decoding/coding order and display order are the same
only 264
总结来说,这篇文档放入tools吧。
最近手上有一块nvidia jetson的板子,在调程序时需要不断的开关机,但是呢总忘记风扇的开启, 对我来说,板子稍微发热我就难受,但是每次开机后手动enable风扇太low,总搜集整理这个文档。
systemd也是大名鼎鼎,目前据说在最新的Ubuntu中已经使用它代替了initd. initd我之前用的比较多了, 但是具体原理没有太多的涉及,这篇文档也是这样的思路,在原理上、技术上不做太多的纠结,直接拿来使用。
可以先看一下这个目录下有很多service后缀的文件,而且大部分是系统文件。例如syslog.service.
此时,我们可以在目录下新建一个文件: rc-local.service,其实这里自定义的名字是无所谓的。其内容可以比着葫芦
画瓢:
[Unit]
Description=/etc/rc.local Compatibility
ConditionPathExists=/etc/rc.local
# Script was located in that
[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99
[Install]
WantedBy=multi-user.target
上面的字面意思也不用太多解释(之所以能够开机自启动在那个 forking)。当然,要记得修改文件权限:
sudo chmod 755 /etc/systemd/system/rc-local.service
这个文件就是开机后具体执行的脚本,我的如下:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
echo "测试脚本执行成功"
echo 100 >> /sys/devices/pwm-fan/target_pwm
exit 0
当然, 需要同样赋予权限:sudo chmod 755 /etc/rc.local.
下面的命令,就是基于rc-local.service文件。
dclab@dclab-desktop:~$ sudo systemctl enable rc-local
[sudo] password for dclab:
启动服务并检查状态
sudo systemctl start rc-local.service
dclab@dclab-desktop:~$ sudo systemctl status rc-local.service
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: e
Drop-In: /lib/systemd/system/rc-local.service.d
└─debian.conf
Active: active (exited) since Tue 2021-01-19 19:59:36 CST; 57min ago
Tasks: 0 (limit: 4915)
CGroup: /system.slice/rc-local.service
Jan 19 19:59:36 dclab-desktop systemd[1]: Starting /etc/rc.local Compatibility...
Jan 19 19:59:36 dclab-desktop rc.local[6314]: 测试脚本执行成功
Jan 19 19:59:36 dclab-desktop systemd[1]: Started /etc/rc.local Compatibility.
Jan 19 20:55:18 dclab-desktop systemd[1]: /etc/systemd/system/rc-local.service:11:
在使用这个脚本一段时间过后,我才发现这里有一个问题就是:不建议使用forking作为启动脚本的hook,很容易引起因为系统资源的问题导致的host hang问题。
原来,自己在很多年前就总结了这块知识
今日,再一次进行更松散的总结。
linker在文件编译时,解决所有的外部符号;当执行时,loader(属于OS)将会重置所有的符号,并且放入合适的内存中。
linux 支持两类库: 静态库和动态库。根据程序编译阶段的不同,又可以分为动态链接库和动态加载库。
共享库没啥特别的地方,也是一种ELF格式文件,和普通的C语言文件没有区别(编译之前),除了没有main()函数入口。当然,要告诉gcc-fPIC选项。
vimer@host:~/test/lib$ cat libfunction.c
int double_me(int a){
return a + a;
}
// gcc -c -fPIC libfunction.c
// gcc -shared -o libmylib.so libfunction.o
vimer@host:~/test/lib$ ls
libfunction.c libfunction.o libmylib.so
当然,也可以综合起来:
gcc -shared -fPIC -o libmylib.so libfunction.c
那么, 在其他文件中,调用这个函数的方法是:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int double_me(int); // 最好放在 .h 文件内
int main(){
int i;
for(i = 0; i < 10; i++)
printf("%d'' s duoble is %d\t", i, double_me(i));
}
我们的main函数在编译阶段会link这个lib。
gcc -o main main.c -L. -lmylib
# -L: path
# -l : 自动加上lib及后缀so
但是,如果你执行试试:
vimer@host:~/test/lib$ ./main
./main: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory
link是没问题的,但是load是无法的。也就是说,尽管我们我们在link阶段指明了so的位置,但是load并不会得知。以上,基本上就是前面文件的内容。
LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
# 必须在一条命令下才可以
So in order to make the loader find our library, we have to tell it where to look, either by adding the correct directory to the LD_LIBRARY_PATH environment variable, or by adding it to the ldconfig paths in either /etc/ld.so.conf inside the /etc/ld.so.conf.d/ directory. Let’s try it with the environment variable for now
如果只想在加载 load 时使用共享库的函数,还是有办法的。
// dynload.c
#include <stdio.h>
#include <dlfcn.h>
int main(void) {
// handle for dynamic loading functions
void *handle;
// function pointer for the library's double_me() function
int (*double_me)(int);
// just a counter
int i;
// open our library ..hopefully
if ( (handle = dlopen("./libmylib.so", RTLD_LAZY)) == NULL) {
return 1;
}
// try to extract "double_me" symbol from the library
double_me = dlsym(handle, "double_me");
if (dlerror() != NULL) {
dlclose(handle);
return 2;
}
// use double_me() just like with a regularly linked library
for (i = 1; i <= 10; i++) {
printf("%d doubled is %d\n", i, double_me(i));
}
dlclose(handle);
return 0;
}
这段代码需要借助libdl动态库, 其实不好的一点就是得猜。
gcc -o dynload dynload.c -ldl
linux中有两种lib,一种是静态的,一种是动态的(Dynamic Shared Object–DSO),由操作系统借助
ld.so或者ld-linux.so在运行时动态load.
/etc/ld.so.cache : File containing an ordered list of libraries found in the directories specified in /*etc/ld.so.conf. This file is not in human readable format, and is not intended to be edited. This file is created by ldconfig command.
在上面的 main 例子中, ldconfig作用在libmylib.so 的目录中并不起作用,不知道为什么。
但是,可以使用下面的命令让共享库生效:
sudo vim /etc/ld.so.conf.d/mylib.conf
vimer@host:~/test/lib$ cat /etc/ld.so.conf.d/mylib.conf
/home/vimer/test/lib # 把libmylib.so所在目录放入这个自己创建的文件,ldconfig一下就可以
vimer@host:~/test/lib$ sudo ldconfig
vimer@host:~/test/lib$ ./main
0'' s duoble is 0 1'' s duoble is 2 2'' s duoble is 4 3'' s duoble is 6 4'' s duoble is 8 5'' s duoble is 10 6'' s duoble is 12 7'' s duoble is 14 8'' s duoble is 16 9'' s duoble is 18
查看ldconfig的内容使用-v选项。
sudo ldconfig -v | grep -i mylib
libmylib.so -> libmylib.so
ldd可以查看一个可执行文件所依赖的动态库
The ld.so or / ld-linux.so used as follows by Linux:
# cd /lib64
vimer@host:/lib64$ ./ld-linux-x86-64.so.2 --list /home/vimer/test/lib/main
linux-vdso.so.1 (0x00007fff48dfa000)
libmylib.so => /home/vimer/test/lib/libmylib.so (0x00007f9654e92000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9654aa1000)
与ldd的作用一样
The LD_LIBRARY_PATH can be used to set a library path for finding dynamic libraries using LD_LIBRARY_PATH, in the standard colon seperated format:
$ export LD_LIBRARY_PATH=/opt/simulator/lib:/usr/local/lib
The LD_PRELOAD allow an extra library not specified in the executable to be loaded:
$ export LD_PRELOAD=/home/vivek/dirhard/libdiehard.so
Please note that these variables are ignored when executing setuid/setgid programs.
pkg-config的作用与ldconfig有些类似,但是pkg-config更侧重在编译、链接阶段;而ldconfig侧重链接、运行时阶段。
一定要限定在普通用户下,或者一定确保你自己执行ps命令不会引出别的进程,不然会引出大麻烦,杀掉系统服务进程或者杀掉别人的进程。
#########################################################################
# File Name: kill_python.sh
# Author: Bo Yu
# mail: [email protected]
# Created Time: 2020年12月25日 星期五 18时57分02秒
##########################################################################
#!/bin/bash
function grep_python(){
tmp_line="ps_txt"
if [ -e $tmp_line ];then
`rm $tmp_line`
else
`touch $tmp_line`
fi
`ps > $tmp_line`
#cat $tmp_line
cat $tmp_line | \
awk '{
for(i=1; i<=NF;i++){
if($i ~ /python/){
cmd="echo "$1" "$4
system(cmd)
cmd="kill -9 "$1
system(cmd)
}
}
}'
}
grep_python
Dockerfile就是指令docker镜像如何建立的一个文件,基本上只有动作,过程的东西基本不要放在里面。
下面的这个Dockerfile就是快速搭建一个Ubuntu18.04的环境,当然,这个没什么可以值得记录的东西,关键是快速更换了国内的镜像源,包括python.
vimer@host:~/test/docker$ cat Dockerfile
FROM ubuntu:18.04
ENV TZ=UTC
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV SOURCES_LIST=/etc/apt/sources.list
# It will fail without cert
#RUN apt update -y && apt install ca-certificates -y
# 更换国内源 @method 1: 但是没有替换全面
# RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
# @method 2: Fill source.list with echo
RUN mv /etc/apt/sources.list /etc/apt/sources-bak.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse" >> $SOURCES_LIST \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse" >> $SOURCES_LIST
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN apt update
RUN mkdir .pip && echo "[global]" >> .pip/pip.conf && echo "https://pypi.tuna.tsinghua.edu.cn/simple/" >> .pip/pip.conf
如果可以的话, 我打算把echo中的内容放在一个文件内,这样就可以快速更新source了。
最后执行 :
docker build -t="demo-video" ./
# 或者
docker build -t "demo-video" -f Dockerfile ./
就可以创建镜像了,使用类似docker run -it --rm image-name就可以run它了。
不过需要注意的是,标签一定设置的正确,不然,你的应用可能用不上。
别的也不说了,还是具体看代码的例子吧。
```bash vimer@host:~/git/videobench$ cat Dockerfile FROM ubuntu:18.04
ENV SOURCES_LIST=/etc/apt/sources.list
RUN mv /etc/apt/sources.list /etc/apt/sources-bak.list
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse” » $SOURCES_LIST
&& echo “deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse” » $SOURCES_LIST
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN apt update
RUN mkdir .pip && echo “[global]” » .pip/pip.conf && echo “https://pypi.tuna.tsinghua.edu.cn/simple/” » .pip/pip.conf
RUN
apt-get install -y build-essential git make
ninja-build
nasm
python3
python3-dev
python3-pip
python3-setuptools
python3-tk
&& pip3 install –upgrade pip
&& pip install numpy scipy matplotlib notebook pandas sympy nose scikit-learn scikit-image h5py sureal meson cython
&& mkdir /tmp/vmaf
&& cd /tmp/vmaf
&& git clone https://github.com/Netflix/vmaf.git .
&& make
&& make install
&& mv model /usr/local/share
&& rm -r /tmp/vmaf
RUN
apt-get install -y yasm pkg-config
&& mkdir /tmp/ffmpeg
&& cd /tmp/ffmpeg
&& git clone https://git.ffmpeg.org/ffmpeg.git .
&& ./configure –enable-libvmaf –enable-version3 –pkg-config-flags=”–static”
&& make -j 8 install
&& rm -r /tmp/ffmpeg
算法学习,是一个持久的斗争,除了思维上的锻炼,更考验意志品质,说白了,对我而言,这不是简单的一个任务,必须持之以恒。
今天关注一下分治算法。个人感觉这个算法比回溯算法要难一些。 当然,形式上看着是非常简单的,但是代码写起来,未必容易理解。
分治法,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
有没有看到,分成子问题的这个过程,肯定借用了递归,可以说二者是形影不离的。
思想: 把大问题分解成规模较小的子问题, 各个击破,分而治之。
策略: 将子问题的解进行合并。
分治法所能解决的问题一般具有以下几个特征:
1) 该问题的规模缩小到一定的程度就可以容易地解决
2) 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
3) 利用该问题分解出的子问题的解可以合并为该问题的解;
4) 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
第一条特征是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加;
第二条特征是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用;、
第三条特征是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法。
第四条特征涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好。
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)合并排序
(6)快速排序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔
以上参考资料
先看一下leetcode 95. Unique Binary Search Trees II,题目的意思是给定一个整型数字n,打印出具有n个节点的二叉搜索树。
二叉搜索树的定义很简单,就是根节点左子树的值都小于根节点,右子树都大于根节点。
怎么思考呢?其实我是没有思路的,这个时候我们应该怎么办,正面突破不容易,得想想办法。我们首先从n等于1开始尝试.
1
/ \
NULL NULL
如果n等于2,则有:
1
/ \
null 2
# and
2
/ \
1 NULL
其实,如果把这两种情况的例子写出来,也许就容易多了,但是,对我来说,这并不容易,只能靠程序量去解决这个问题。 先上一个优秀的代码,看一下这个代码是怎么写的。
class Solution {
private:
vector<TreeNode*> helper(int start, int end){ // 这两个很好
vector<TreeNode*> res;
if(start > end){
res.push_back(NULL);
return res;
}
for(int i = start; i <= end; i++){
vector<TreeNode*> lefts = helper(start, i-1);
vector<TreeNode*> rights = helper(i+1, end);
for(int j = 0; j < (int)lefts.size(); j++)
for(int k = 0; k < (int)rights.size(); k++){
TreeNode *root = new TreeNode(i);
root->left = lefts[j];
root->right = rights[k];
res.push_back(root);
}
}
return res;
}
public:
vector<TreeNode*> generateTrees(int n) {
if(n == 0) return vector<TreeNode*>(0);
return helper(1,n);
}
};
先来看一下n=1的情况,当helper(1,1)传入时,
vector<TreeNode*> lefts = helper(start, i-1);
vector<TreeNode*> rights = helper(i+1, end);
都会返回一个NULL,并且加入进res中(也就是res.size()为1),然后在双重循环中,new TreeNode(1),一定使用new去实例化。这个时候,左右子树都为NULL,最后将root压入res中。
当n=2是,helper(1,2)传入,首先i=1, lefts = helper(start, i-1)将会返回一个NULL,而rights则有些变化了。helper(2,2),并不会返回一个NULL,而是返回一个只有值为2的节点。那么,到了二重for语句那里,会实例化出(i=1)作为root,然后左孩子为NULL,右孩子为节点2.这里还需要注意一个语句,root->left = lefts[j],这个为啥需要[j],字面上的意思肯定是深度。这样,就完成了一个。
然后i=2时,重复上面的过程,lefts = helper(1,1),所以lefts是只有一个节点值为1的节点。而此时的rights是一个NULL,
那么,在二重for,会实例化(i=2),并把1节点lefts、null节点rights赋给root,最后加入到res中。
n=1,2都好理解,当n=3时就麻烦一些了,但是,我们以其中一个例子说明。当helper(1,3)传入时,首先得到的是lefts = helper(1,0)和rights = helper(2,3),(i=1)这是第一层的。显而易见,helper(1,0)只会返回一个NULL, 还是要注意rights = helper(2,3)(i=2),那么rights会分裂为lefts = helper(2,1)(NULL), rights=helper(3,3),这是第二层;rights=heler(3,3)返回节点3,在第三层,可以不用管了。那么在第二层中i=2时,有了lefts=NULL和rights=3,在new TreeNode(2)后,lefts和rights分别赋值给root(2).这里需要注意的是,这个
2
/ \
NULL 3
是在第一层的rights=helper(2,3)得来的,此时i=1,new TreeNode(1),左孩子为NULL, 右孩子为上图,符合题意。
当i=2时, 会分裂为lefts=helper(1,1)和rights=helper(3,3),这个就简单了。
2
/ \
1 3