FFmpeg 实现从摄像头获取流并通过RTMP推流
使用FFmpeg库实现从USB摄像头获取流并通过RTMP推流,FFmpeg版本为4.4.2-0。RTMP服务器使用的是SRS,拉流端使用VLC。
在Linux上查看摄像头信息可使用 v4l2-ctl 工具,查看命令如下:
v4l2-ctl --device=/dev/video0 --list-formats-ext
代码如下:
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>int main(void)
{const char *input_format_name = "video4linux2"; // 输入格式名称,Linux下为video4linux2或v4l2const char *device_name = "/dev/video0"; // 摄像头设备名称const char *camera_resolution = "640x480"; // 摄像头分辨率enum AVPixelFormat camera_pix_fmt = AV_PIX_FMT_YUYV422; // 摄像头像素格式const char *url = "rtmp://192.168.3.230/live/livestream"; // rtmp地址int frame_rate = 25; // 帧率int ret = -1, retVal = -1;int video_streamid = -1;int64_t frame_index = 0;AVDictionary *options = NULL;AVInputFormat *fmt = NULL;AVFormatContext *in_context = NULL, *out_context = NULL;struct SwsContext *sws_ctx = NULL;AVCodecContext *codec_context = NULL;AVStream *out_stream = NULL;AVCodec *codec = NULL;AVPacket *packet = NULL;// 注册所有设备avdevice_register_all();// 查找输入格式fmt = av_find_input_format(input_format_name);if (!fmt){fprintf(stderr, "av_find_input_format error");return -1;}// 设置分辨率av_dict_set(&options, "video_size", camera_resolution, 0);// 打开输入流并初始化格式上下文retVal = avformat_open_input(&in_context, device_name, fmt, &options);if (retVal != 0){// 打印错误信息fprintf(stderr, "avformat_open_input error");return -1;}// 查找视频流索引video_streamid = av_find_best_stream(in_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if (video_streamid < 0){fprintf(stderr, "cannot find video stream");goto end;}AVStream *video_stream = in_context->streams[video_streamid];printf("input stream, width: %d, height: %d, format: %s\n",video_stream->codecpar->width, video_stream->codecpar->height,av_get_pix_fmt_name((enum AVPixelFormat)video_stream->codecpar->format));// 检查实际获取到的格式是否为设置的摄像头像素格式if (video_stream->codecpar->format != camera_pix_fmt){fprintf(stderr, "pixel format error");goto end;}// 初始化转换上下文sws_ctx = sws_getContext(video_stream->codecpar->width, video_stream->codecpar->height, camera_pix_fmt,video_stream->codecpar->width, video_stream->codecpar->height, AV_PIX_FMT_YUV420P,SWS_BILINEAR, NULL, NULL, NULL);if (!sws_ctx){fprintf(stderr, "sws_getContext error\n");goto end;}// 创建帧AVFrame *input_frame = av_frame_alloc();AVFrame *frame_yuv420p = av_frame_alloc();if (!input_frame || !frame_yuv420p){fprintf(stderr, "av_frame_alloc error\n");goto end;}// 设置帧格式input_frame->format = camera_pix_fmt;input_frame->width = video_stream->codecpar->width;input_frame->height = video_stream->codecpar->height;frame_yuv420p->format = AV_PIX_FMT_YUV420P;frame_yuv420p->width = video_stream->codecpar->width;frame_yuv420p->height = video_stream->codecpar->height;// 分配帧内存retVal = av_frame_get_buffer(frame_yuv420p, 0);if (retVal < 0){fprintf(stderr, "av_frame_get_buffer error\n");goto end;}// 分配输出格式上下文avformat_alloc_output_context2(&out_context, NULL, "flv", NULL);if (!out_context){fprintf(stderr, "avformat_alloc_output_context2 failed\n");goto end;}// 查找编码器codec = avcodec_find_encoder(AV_CODEC_ID_H264);if (!codec){fprintf(stderr, "Codec not found\n");goto end;}printf("codec name: %s\n", codec->name);// 创建新的视频流out_stream = avformat_new_stream(out_context, NULL);if (!out_stream){fprintf(stderr, "avformat_new_stream failed\n");goto end;}// 分配编码器上下文codec_context = avcodec_alloc_context3(codec);if (!codec_context){fprintf(stderr, "avcodec_alloc_context3 failed\n");goto end;}// 设置编码器参数codec_context->codec_id = AV_CODEC_ID_H264;codec_context->codec_type = AVMEDIA_TYPE_VIDEO;codec_context->pix_fmt = AV_PIX_FMT_YUV420P;codec_context->width = frame_yuv420p->width;codec_context->height = frame_yuv420p->height;codec_context->time_base = (AVRational){1, frame_rate}; // 设置时间基codec_context->framerate = (AVRational){frame_rate, 1}; // 设置帧率codec_context->bit_rate = 750 * 1000; // 设置比特率codec_context->gop_size = frame_rate; // 设置GOP大小codec_context->max_b_frames = 0; // 设置最大B帧数,不需要B帧时设置为0av_opt_set(codec_context->priv_data, "profile", "baseline", 0); // 设置h264画质级别av_opt_set(codec_context->priv_data, "tune", "zerolatency", 0); // 设置h264编码优化参数// 检测输出上下文的封装格式,判断是否设置 AV_CODEC_FLAG_GLOBAL_HEADER// AV_CODEC_FLAG_GLOBAL_HEADER:由原来编码时在每个关键帧前加入pps和sps,改变为在extradate这个字节区加入pps和spsif (out_context->oformat->flags & AVFMT_GLOBALHEADER){printf("set AV_CODEC_FLAG_GLOBAL_HEADER\n");codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;}// 打开编码器if (avcodec_open2(codec_context, codec, NULL) < 0){fprintf(stderr, "avcodec_open2 failed\n");goto end;}// 将编码器参数复制到流int result = avcodec_parameters_from_context(out_stream->codecpar, codec_context);if (result < 0){fprintf(stderr, "avcodec_parameters_from_context failed\n");goto end;}// 打开urlif (!(out_context->oformat->flags & AVFMT_NOFILE)){result = avio_open(&out_context->pb, url, AVIO_FLAG_WRITE);if (result < 0){fprintf(stderr, "avio_open error (errmsg '%s')\n", av_err2str(result));goto end;}}// 写文件头result = avformat_write_header(out_context, NULL);if (result < 0){fprintf(stderr, "avformat_write_header failed\n");goto end;}packet = av_packet_alloc();if (!packet){fprintf(stderr, "av_packet_alloc failed\n");goto end;}// 读取帧并进行转换AVPacket pkt;while (av_read_frame(in_context, &pkt) >= 0){if (pkt.stream_index == video_streamid){// 把读取的帧数据(AVPacket)拷贝到输入帧(AVFrame)中retVal = av_image_fill_arrays(input_frame->data, input_frame->linesize, pkt.data, camera_pix_fmt,video_stream->codecpar->width, video_stream->codecpar->height, 1);if (retVal < 0){av_packet_unref(&pkt);fprintf(stderr, "av_image_fill_arrays error\n");break;}// 转换为 YUV420Psws_scale(sws_ctx, (const uint8_t *const *)input_frame->data, input_frame->linesize, 0,input_frame->height, frame_yuv420p->data, frame_yuv420p->linesize);frame_yuv420p->pts = frame_index;frame_index++;// 发送帧到编码器result = avcodec_send_frame(codec_context, frame_yuv420p);if (result < 0){fprintf(stderr, "avcodec_send_frame error (errmsg '%s')\n", av_err2str(result));break;}// 接收编码后的数据包while (result >= 0){result = avcodec_receive_packet(codec_context, packet);if (result == AVERROR(EAGAIN) || result == AVERROR_EOF){break;}else if (result < 0){fprintf(stderr, "avcodec_receive_packet error (errmsg '%s')\n", av_err2str(result));goto end;}packet->stream_index = out_stream->index;// 将时间戳从编码器时间基转换到流时间基av_packet_rescale_ts(packet, codec_context->time_base, out_stream->time_base);packet->pos = -1;// 推送到RTMP服务器result = av_interleaved_write_frame(out_context, packet);if (result < 0){fprintf(stderr, "av_interleaved_write_frame error (errmsg '%d')\n", result);av_packet_unref(packet);goto end;}av_packet_unref(packet);}}av_packet_unref(&pkt);}end:// 释放资源if (input_frame)av_frame_free(&input_frame);if (frame_yuv420p)av_frame_free(&frame_yuv420p);if (sws_ctx)sws_freeContext(sws_ctx);if (in_context)avformat_close_input(&in_context);if (packet)av_packet_free(&packet);if (codec_context)avcodec_free_context(&codec_context);if (out_context)avformat_free_context(out_context);return ret;
}
相关文章:
FFmpeg 实现从摄像头获取流并通过RTMP推流
使用FFmpeg库实现从USB摄像头获取流并通过RTMP推流,FFmpeg版本为4.4.2-0。RTMP服务器使用的是SRS,拉流端使用VLC。 在Linux上查看摄像头信息可使用 v4l2-ctl 工具,查看命令如下: v4l2-ctl --device/dev/video0 --list-formats-e…...
学生管理系统
一、登录 用户类:属性:用户名、密码、身份证号码、手机号码 1、欢迎页面 System.out.println("欢迎来到学生管理系统"); System.out.println("请选择操作1登录 2注册 3忘记密码"); 代码实现: //欢迎页面public static…...
【linux】网络基础(3)——tcp协议
文章目录 TCP协议概括TCP头部格式TCP连接管理建立连接(三次握手)数据传输确认应答机制捎带应答 滑动窗口丢包问题 拥塞控制延时应达 终止连接(四次挥手) TCP协议概括 TCP是一个面向连接的协议,在传输数据之前需要建立连…...
[Day 21] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
區塊鏈的智能合約運行機制 區塊鏈技術自比特幣誕生以來,便以其去中心化、安全性和透明性等特點引起了廣泛的關注和應用。而智能合約作為區塊鏈技術的一大創新,進一步擴展了區塊鏈的應用場景,使其不僅僅局限於數字貨幣,還可以應用…...
使用ps给gif动图抠图
目录 导入gif图片 打开时间轴 选择图片 魔棒抠图-初步抠图 套索抠图-精准抠图 导入gif图片 打开时间轴 因为gif动图实际上多张图片实现的效果,所以如果要给gif抠图,就得挨个给每个时间线的图片抠图 点击窗口->时间轴 选择图片 在时间轴上选择要…...
pmp顺利通关总结
目录 一、背景二、总结三、过程 一、背景 人活着总是想去做一些事情,通过这些事情来证明自己还活着。 而我证明自己还会活着并且活得很好的方式和途径,是通过这些东西去让自己有一个明确的边界节点;借此知识来验证自己的学习能力。 我坚定认…...
未来的钥匙在于过去:学历史的真正意义,震惊!历史竟然是偶然的?从历史中寻找未来的方向!
我们自幼接受的教育是,学历史是为了相信历史是必然的。中国人民必然战胜日寇的侵略,解放思想和改革开放必定会发生,和平和发展必定是世界的主题,中国经济必定是高速增长…… 然而,在真正的历史学家眼中,历史…...
ES6自定义模块
在ES6中,我们可以使用 export 和 import 关键字来定义和使用自定义模块。 定义模块 导出(export) 命名导出(Named Exports): 使用 export 关键字来导出模块中的变量、函数、类等。例如: // ma…...
Windows页面错误(Page Fault)写几种c++会导致,此问题的例子
在C中,直接导致Windows页面错误(Page Fault)的情景较少直接由编程错误引发,页面错误更多是由操作系统在内存管理和虚拟内存机制中处理的。不过,某些编程错误可能导致访问违规,进而间接引起操作系统报告页面…...
AC7801时钟配置流程
一 默认配置 在启动文件中,已经对时钟进行了初始化,默认按外部8M晶振,配置系统时钟为48MHZ,APB为系统时钟的2分频,为24MHZ。在system_ac780x.c文件中,可以找到下面这个系统初始化函数,里面有Se…...
加密与安全_Java 加密体系 (JCA) 和 常用的开源密码库
文章目录 Java Cryptography Architecture (JCA)开源国密库国密算法对称加密(DES/AES⇒SM4)非对称加密(RSA/ECC⇒SM2)散列(摘要/哈希)算法(MD5/SHA⇒SM3) 在线生成公钥私钥对,RSA公私钥生成参考…...
读书笔记-《Spring技术内幕》(三)MVC与Web环境
前面我们学习了 Spring 最核心的 IoC 与 AOP 模块(读书笔记-《Spring技术内幕》(一)IoC容器的实现、读书笔记-《Spring技术内幕》(二)AOP的实现),接下来继续学习 MVC,其同样也是经典…...
k8s及常用对象简介
文章目录 一、k8s是什么应用程序早期部署形式容器的引入k8s的作用 二、k8s中的常用对象1、Node获取node信息 2、Namespacenamespace的使用 3、Pod生命周期pod的使用 4、DaemonSetDaemonSet的使用 5、Deployment创建deploy 6、ReplicaSet7、StatefulSet创建StatefulSet 8、更新操…...
HTTPS数字证书验证论述
1 概述 网络请求方式通常分为两种,分别是HTTP请求和HTTPS请求,其中HTTP的传输属于明文传输,在传输的过程中容易被人截取并且偷窥其中的内容,而HTTPS是一种在HTTP的基础上加了SSL/TLS层(安全套接层)的安全的…...
【高考志愿】地质资源与地质工程
目录 一、专业概述 1.1 专业定义 1.2 主要课程 1.3 专业培养目标 二、就业前景和考研方向 三、工作特点和挑战 四、如何培养核心竞争力 五、 地质资源与地质工程专业排名 六、结语 关于高考志愿选择地质资源与地质工程专业,以下是一些详细的介绍和参考信息…...
全网最佳硕士研究生复试简历模板
硕士研究生复试简历模板 ✨ 简介 提供了一个适用于国内硕士研究生复试的个人简历模板。该模板通过统一的“样式”形成规范的Word格式,是目前研究生复试的最佳简历模板之一。模板使用“华文中宋”字体,如您的电脑中未安装此字体,请提前安装。…...
Rocky Linux 9 系统OpenSSH CVE-2024-6387 漏洞修复
Rocky Linux 9系统 OpenSSH CVE-2024-6387 漏洞修复 1、漏洞修复2、修复思路3、修复方案3.1、方案一3.2、方案二 4、总结5、参考 1、漏洞修复 CVE-2024-6387:regreSSHion:OpenSSH 服务器中的远程代码执行(RCE),至少在…...
Sping源码(九)—— Bean的初始化(非懒加载)—mergeBeanDefinitionPostProcessor
序言 前几篇文章详细介绍了Spring中实例化Bean的各种方式,其中包括采用FactoryBean的方式创建对象、使用反射创建对象、自定义BeanFactoryPostProcessor以及构造器方式创建对象。 创建对象 这里再来简单回顾一下对象的创建,不知道大家有没有这样一个疑…...
labview技巧——AMC框架安装
AMC工具包的核心概念是队列,队列是一种先进先出(FIFO,First In First Out)的数据结构,适用于处理并发和异步任务。在LabVIEW中,队列可以用于在不同VI之间传递数据,确保消息的有序处理࿰…...
解锁分布式云多集群统一监控的云上最佳实践
作者:在峰 引言 在当今数字化转型加速的时代,随着混合云、多云多集群环境等技术被众多企业广泛应用,分布式云架构已成为众多企业和组织推动业务创新、实现弹性扩展的首选,分布式云容器平台 ACK One(Distributed Clou…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
