参考:https://blog.csdn.net/weixin_42432281/article/details/88348124
本文的目的是实现linux嵌入式端ffmpeg读取网络相机rtsp码流,不经过编解码操作,直接保存为视频文件,ffmpeg有命令的形式可以实现(参考《FFmpeg功能命令汇总》),但是无法灵活设置,而且调用起来也不方便,因此直接使用源码编译,提供功能接口
通常所说的rtsp协议其实包含三个协议: rtsp协议, rtp协议, rtcp协议。各协议运作流程概要:
第一阶段:rtsp协议负责沟通传输什么数据,传的是图像还是声音,还是两者混合?图像的话传是h264流,还是h265流,还是jpeg流?后续的rtp,rtcp协议是采用tcp还是udp,端口号是多少都是通过第一阶段的rtsp协议确定的。
第二阶段:通过rtp协议传输数据,rtcp进行网络传输质量的监控
第三阶段:通过rtsp协议中断整个协议的传输
首先先将设备上的ffmpeg卸载干净,防止后续出错
sudo apt-get remove ffmpeg x264
sudo apt-get autoremove
因为要保存成h264文件,需要有h264编解码器,安装ffmpeg的时候也必须要选择支持x264
git clone https://code.videolan.org/videolan/x264.git
cd x264
//如果git指令下载速度特别慢的话,可以在下边链接里下载压缩包
//https://www.videolan.org/developers/x264.html
./configure --enable-shared --enable-static --prefix=/usr/local
sudo make
sudo make install
依次按照如下指令,注意由于我是在arm嵌入式板子上安装的,所以第3步选择的是arm-linux,自己要结合实际情况来选择,后续的操作方法是一样的
$ sudo apt-get install mercurial cmake cmake-curses-gui build-essential yasm
$ hg clone https://bitbucket.org/multicoreware/x265
$ cd x265/build/arm-linux
$ sudo sh ./make-Makefiles.bash
# 要把PIC选项打开,见下图,按下“g”退出
$ sudo make
$ sudo make install
这里推荐采用源码安装,网上很多人推荐的apt安装方式装完之后,有些文件和文件夹找不到,使用源码安装,可以自己配置安装路径,更适合我们源码开发
下载地址:https://ffmpeg.org
下载后解压,使用如下指令安装
# ./configure --enable-libx264 --enable-libx265 --enable-shared --enable-gpl --prefix=/usr/local
# sudo make
# sudo make install
安装完后运行ldd `which ffmpeg` 查看ffmpeg的库是否可以找到
这时一般会打印为:
linux-gate.so.1 => (0x00f90000)
libavdevice.so.52 => not found
libavfilter.so.1 => not found
libavformat.so.52 => not found
libavcodec.so.52 => not found
libpostproc.so.51 => not found
libswscale.so.0 => not found
libavutil.so.50 => not found
libpthread.so.0 => /lib/libpthread.so.0 (0x00821000)
libm.so.6 => /lib/libm.so.6 (0x007da000)
libc.so.6 => /lib/libc.so.6 (0x0067b000)
/lib/ld-linux.so.2 (0x0065c000)
而命令find /usr/local/lib/ | grep -E "libavdevice.so.52|libavfilter.so.1|libavcodec.so.52|libavcore.so.0"
通常会打印输出
/usr/local/lib/libavdevice.so.52.5.0
/usr/local/lib/libavfilter.so.1
/usr/local/lib/libavdevice.so.52
/usr/local/lib/libavfilter.so.1.80.0
/usr/local/lib/libavcodec.so.52.123.0
/usr/local/lib/libavcodec.so.52
说明你有这些库,只是没有识别。当然,路径/usr/local/lib 和/usr/local/include主要是因为ffmpeg的安装前缀是/usr/lcoal,好像用户安装linux的软件默认都是这里。把ffmpeg库路径加入环境变量
sudo vi /etc/ld.so.conf
/usr/local/lib //在文本后边添加该路径
sudo ldconfig //执行ldconfig立即生效
新建一个文件video2h264.c,内容如下
#include
#include
#ifdef __cplusplus
extern "C"
{
#endif
/*Include ffmpeg header file*/
#include
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
}
#endif
void main()
{
AVFormatContext *pFormatCtx;
char filepath[] = "rtsp://admin:[email protected]:554/h264/ch1/main/av_stream";
AVPacket *packet;
//初始化
av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context();
AVDictionary* options = NULL;
av_dict_set(&options, "buffer_size", "1024000", 0); //设置缓存大小,1080p可将值跳到最大
av_dict_set(&options, "rtsp_transport", "tcp", 0); //以udp的方式打开,
av_dict_set(&options, "stimeout", "5000000", 0); //设置超时断开链接时间,单位us
av_dict_set(&options, "max_delay", "500000", 0); //设置最大时延
packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//打开网络流或文件流
if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0)
{
printf("Couldn't open input stream.\n");
return;
}
//查找码流信息
if (avformat_find_stream_info(pFormatCtx, NULL)<0)
{
printf("Couldn't find stream information.\n");
return;
}
//查找码流中是否有视频流
int videoindex = -1;
unsigned i = 0;
for (i = 0; inb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
break;
}
if (videoindex == -1)
{
printf("Didn't find a video stream.\n");
return;
}
//保存一段的时间视频,写到文件中
FILE *fpSave;
fpSave = fopen("geth264_test.h264", "wb");
for (i = 0; i < 20000; i++) //这边可以调整i的大小来改变文件中的视频时间
{
if (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == videoindex)
{
fwrite(packet->data, 1, packet->size, fpSave);
}
av_packet_unref(packet);
}
}
fclose(fpSave);
av_free(pFormatCtx);
//av_free(packet);
av_packet_unref(packet);
}
编译指令
gcc video2h264.c -o ffmpegtest -lpthread -lm -lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswresample -lswscale -lrt -lx264 -ldl
如果编译时报错如下
In file included from /usr/local/include/libavutil/avutil.h:296:0,
from /usr/local/include/libavutil/samplefmt.h:24,
from /usr/local/include/libavcodec/avcodec.h:31,
from video2mp4.cpp:5:
/usr/local/include/libavutil/common.h:31:2: error: #error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS
In file included from /usr/local/include/libavutil/avutil.h:296:0,
from /usr/local/include/libavutil/samplefmt.h:24,
from /usr/local/include/libavcodec/avcodec.h:31,
from video2mp4.cpp:5:
/usr/local/include/libavutil/common.h: In function ‘int32_t av_clipl_int32_c(int64_t)’:
/usr/local/include/libavutil/common.h:209:47: error: ‘UINT64_C’ was not declared in this scope
则需要修改common.h文件,运行
sudo vi /usr/local/include/libavutil/common.h
在在#error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS前面前边添加如下
#if defined __cplusplus
#define __STDC_CONSTANT_MACROS
#endif
#ifndef UINT64_C
#define UINT64_C(value) __CONCAT(value, ULL)
#endif
保存退出即可