当前位置: 首页 > news >正文

FFmpeg中AVIOContext的使用

      通过FFmpeg对视频进行编解码时,如果输入文件存在本机或通过USB摄像头、笔记本内置摄像头获取数据时,可通过avformat_open_input接口中的第二个参数直接指定即可。但如果待处理的视频数据存在于内存块中时,该如何指定,可通过FFmpeg中的结构体AVIOContext实现,此时avformat_open_input中的第二个参数传nullptr。

      涉及到FFmpeg中的主要函数是avio_alloc_context,声明如下:

AVIOContext *avio_alloc_context(unsigned char *buffer,int buffer_size,int write_flag,void *opaque,int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),int64_t (*seek)(void *opaque, int64_t offset, int whence))

      (1).buffer:通过AVIOContext进行输入/输出操作的内存块,由av_malloc分配,av_free释放。av_read_frame会持续从此处取数据。
      (2).buffer_size: 内存块大小。
      (3).write_flag: 如果buffer作为输出即写入则为1(FFmpeg将处理后的数据写入buffer),如果buffer作为输入则设置为0(FFmpeg从buffer获取数据).
      (4).opaque: 指向用户特定数据的不透明指针。
      (5).read_packet: 回调函数,当buffer作为输入时必须指定,否则可为nullptr。此回调函数的参数依次为avio_alloc_context中的opaque、buffer、buffer_size。
      (6).write_packet:回调函数,当buffer作为输出时必须指定,否则可为nullptr。此回调函数的参数依次为avio_alloc_context中的opaque、buffer、buffer_size。
      (7).seek:回调函数,用于查找指定字节位置的函数,可为nullptr。
      调用完此接口后,需要将此接口返回的指针赋值给AVFormatContext的pb即I/O context。
      以下为测试代码段:

      (1).主线程用于实时显示内存块内容。另有一个单独线程用于创建数据。这里使用队列:线程set_packet持续向队列中push数据;回调函数read_packet持续从队列中pop数据

typedef struct Buffer {unsigned char* data;unsigned int length;
} Buffer;class BufferQueue {
public:BufferQueue() = default;~BufferQueue() {}void push(Buffer& buffer) {std::unique_lock<std::mutex> lck(mtx);queue.push(buffer);cv.notify_all();}void pop(Buffer& buffer) {std::unique_lock<std::mutex> lck(mtx);while (queue.empty()) {cv.wait(lck);}buffer = queue.front();queue.pop();}unsigned int size() {return queue.size();}private:std::queue<Buffer> queue;std::mutex mtx;std::condition_variable cv;
};class PacketScaleQueue {
public:PacketScaleQueue() = default;~PacketScaleQueue() {Buffer buffer;while (getPacketSize() > 0) {popPacket(buffer);delete[] buffer.data;}while (getScaleSize() > 0) {popScale(buffer);delete[] buffer.data;}}void init(unsigned int buffer_num = 16, unsigned int buffer_size = 1024 * 1024 * 4) {for (unsigned int i = 0; i < buffer_num; ++i) {Buffer buffer = { new unsigned char[buffer_size], buffer_num};pushPacket(buffer);}}void pushPacket(Buffer& buffer) { packet_queue.push(buffer); }void popPacket(Buffer& buffer) { packet_queue.pop(buffer); }unsigned int getPacketSize() { return packet_queue.size(); }void pushScale(Buffer& buffer) { scale_queue.push(buffer); }void popScale(Buffer& buffer) { scale_queue.pop(buffer); }unsigned int getScaleSize() { return scale_queue.size(); }private:BufferQueue packet_queue, scale_queue;
};

      (2).线程函数set_packet内容如下:类PacketScaleQueue中有两个BufferQueue: packet_queue:未被使用的;scale_queue:已被使用的

void set_packet(PacketScaleQueue& packet_encode)
{while (packet_encode_flag) {static unsigned char v1 = 0, v2 = 0, v3 = 255;static const size_t size = height * width;Buffer buffer;packet_encode.popPacket(buffer);memset(buffer.data, v1, size);memset(buffer.data + size, v2, size);memset(buffer.data + size * 2, v3, size);packet_encode.pushScale(buffer);++v1;++v2;--v3;if (v1 == 255) v1 = 0;if (v2 == 255) v2 = 0;if (v3 == 0) v3 = 255;std::this_thread::sleep_for(std::chrono::milliseconds(40));}
}

      (3).回调函数read_packet内容如下:

int read_packet(void* opaque, uint8_t* buf, int buf_size)
{PacketScaleQueue* packet_encode = static_cast<PacketScaleQueue*>(opaque);Buffer buffer;packet_encode->popScale(buffer);memcpy(buf, buffer.data, buf_size);packet_encode->pushPacket(buffer);return buf_size;
}

      (4).主函数test_ffmpeg_avio_show内容如下:

int test_ffmpeg_avio_show()
{PacketScaleQueue packet_encode;packet_encode.init(30, block_size);std::thread thread_packet(set_packet, std::ref(packet_encode));uint8_t* avio_ctx_buffer = static_cast<uint8_t*>(av_malloc(block_size));if (!avio_ctx_buffer) {print_error_string(AVERROR(ENOMEM));return -1;}AVIOContext* avio_ctx = avio_alloc_context(avio_ctx_buffer, block_size, 0, &packet_encode, &read_packet, nullptr, nullptr);if (!avio_ctx) {print_error_string(AVERROR(ENOMEM));return -1;}AVFormatContext* ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx) {print_error_string(AVERROR(ENOMEM));return -1;}ifmt_ctx->pb = avio_ctx;AVDictionary* dict = nullptr;av_dict_set(&dict, "video_size", "640x480", 0);av_dict_set(&dict, "pixel_format", "bgr24", 0);auto ret = avformat_open_input(&ifmt_ctx, nullptr, av_find_input_format("rawvideo"), &dict);if (ret < 0) {fprintf(stderr, "Could not open input\n");print_error_string(ret);return ret;}ret = avformat_find_stream_info(ifmt_ctx, nullptr);if (ret < 0) {fprintf(stderr, "Could not find stream information\n");print_error_string(ret);return ret;}av_dump_format(ifmt_ctx, 0, "nothing", 0);int video_stream_index = -1;for (unsigned int i = 0; i < ifmt_ctx->nb_streams; ++i) {const AVStream* stream = ifmt_ctx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_stream_index = i;fprintf(stdout, "type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);}}if (video_stream_index == -1) {fprintf(stderr, "error: no video stream\n");return -1;}AVCodecParameters* codecpar = ifmt_ctx->streams[video_stream_index]->codecpar;if (codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {fprintf(stderr, "error: this test code only support rawvideo encode: %d\n", codecpar->codec_id);return -1;}AVPacket* packet = static_cast<AVPacket*>(av_malloc(sizeof(AVPacket)));if (!packet) {fprintf(stderr, "fail to av_malloc\n");return -1;}cv::Mat mat(height, width, CV_8UC3);const char* winname = "show video";cv::namedWindow(winname);while (1) {ret = av_read_frame(ifmt_ctx, packet);if (ret >= 0 && packet->stream_index == video_stream_index && packet->size > 0) {mat.data = packet->data;cv::imshow(winname, mat);av_packet_unref(packet);int key = cv::waitKey(30);if (key == 27) {packet_encode_flag = false;break;}}}av_freep(packet);cv::destroyWindow(winname);avformat_close_input(&ifmt_ctx);// note: the internal buffer could have changed, and be != avio_ctx_bufferif (avio_ctx) {av_freep(&avio_ctx->buffer);av_freep(&avio_ctx);}//avio_context_free(&avio_ctx); ==> av_freep(&avio_ctx);av_dict_free(&dict);thread_packet.join();fprintf(stdout, "test finish\n");return 0;
}

      执行结果如下图所示:

      GitHub:https://github.com/fengbingchun/OpenCV_Test

相关文章:

FFmpeg中AVIOContext的使用

通过FFmpeg对视频进行编解码时&#xff0c;如果输入文件存在本机或通过USB摄像头、笔记本内置摄像头获取数据时&#xff0c;可通过avformat_open_input接口中的第二个参数直接指定即可。但如果待处理的视频数据存在于内存块中时&#xff0c;该如何指定&#xff0c;可通过FFmpeg…...

【react】react中BrowserRouter和HashRouter的区别:

文章目录 1.底层原理不一样:2.path衣现形式不一样3.刷新后对路山state参数的影响4.备注: HashRouter可以用于解决一些路径错误相关的问题 1.底层原理不一样: BrowserRouter使用的是H5的history API&#xff0c;不兼容IE9及以下版不。 HashRouter使用的是URL的哈希值。 2.path衣…...

机器学习常用Python库安装

机器学习常用Python库安装 作者日期版本说明Dog Tao2022.06.16V1.0开始建立文档 文章目录 机器学习常用Python库安装Anaconda简介使用镜像源配置 Pip简介镜像源配置 CUDAPytorch安装旧版本 TensorFlowGPU支持说明 DGL简介安装DGLLife RDKitscikit-multilearn Anaconda 简介 …...

HTTP 劫持、DNS 劫持与 XSS

HTTP 劫持、DNS 劫持与 XSS http 劫持是指攻击者在客户端和服务器之间同时建立了连接通道&#xff0c;通过某种方式&#xff0c;让客户端请求发送到自己的服务器&#xff0c;然后自己就拥有了控制响应内容的能力&#xff0c;从而给客户端展示错误的信息&#xff0c;比如在页面中…...

bash引用-Quoting详细介绍

bash引用-Quoting详细介绍 概述 引用的字面意思就是&#xff0c;用引号括住一个字符串。这可以保护字符串中的特殊字符不被shell或shell脚本重新解释或扩展。(如果一个字有不同于其字面意思的解释&#xff0c;它就是“特殊的”。例如&#xff1a;星号*除了本身代表*号以外还表…...

powershell几句话设置环境变量

设置环境变量比较繁琐&#xff0c;现在用这段话&#xff0c;在powershell中就可以轻松完成。 $existingPath [Environment]::GetEnvironmentVariable("Path", "Machine") $newPath "C:\Your\Path\Here"if ($existingPath -split ";"…...

Javascript 数据结构[入门]

作者&#xff1a;20岁爱吃必胜客&#xff08;坤制作人&#xff09;&#xff0c;近十年开发经验, 跨域学习者&#xff0c;目前于海外某世界知名高校就读计算机相关专业。荣誉&#xff1a;阿里云博客专家认证、腾讯开发者社区优质创作者&#xff0c;在CTF省赛校赛多次取得好成绩。…...

IO(JavaEE初阶系列8)

目录 前言&#xff1a; 1.文件 1.1认识文件 1.2结构和目录 1.3文件路径 1.4文本文件vs二进制文件 2.文件系统的操作 2.1Java中操作文件 2.2File概述 2.2.1构造File对象 2.2.2File中的一些方法 3.文件内容的操作 3.1字节流 3.1.1InPutStream的使用方法 3.1.2OutPu…...

React Native 样式表的基础知识

在 React Native 中我们要使用组件元素进行样式设置的话&#xff0c;我们需要使用StyleSheet组件才能制定样式。useColorScheme是为 APP 定义颜色主题的。在此笔记中我们只是简单做一个介绍和使用。 使用StyleSheet定义样式 当我们要使用StyleSheet的话&#xff0c;我们需要引…...

【JS 解构赋值】

JS 解构赋值是 ES6 中一种简洁、高效的赋值方式&#xff0c;它可以将数组和对象中的值拆分出来并赋值给变量。 解构赋值 解构数组解构对象嵌套解构结语 解构数组 解构数组时&#xff0c;需要使用方括号 [] 包围变量名&#xff0c;并用逗号 , 将变量名隔开。 let [a, b, c] …...

Vue3状态管理库Pinia——自定义持久化插件

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…...

il汇编整数相加

在这里尝试了IL汇编字符串连接&#xff1b; IL汇编字符串连接_bcbobo21cn的博客-CSDN博客 下面来看一下IL汇编整数相加&#xff1b; 大概的看一下一些资料&#xff0c;下面语句&#xff0c; ldc.i4 20 ldc.i4 30 add 看上去像是&#xff0c;装载整数20到一个类似于…...

RabbitMQ 事务

事务简介 就像我们了解的MySQL中的事务一样&#xff0c;RabbiMQ的事务也具备原子性和一致性&#xff0c;并且RabbiMQ的事务是针对消息从生产者发送到RabbitMQ中提供的支持&#xff0c;因此不同事务可以同时给同一个队列发送信息。   可通过channel.txSelect&#xff0c;chann…...

vue前端 让年月日 加上23:59:59

yyyy/MM/dd HH:mm:ss 格式 // 获取 lateCreateTime 的原始时间戳 const timestamp new Date(this.queryAO.lateCreateTime).getTime();// 将时间戳转换为指定格式的字符串 const formattedDateTime new Date(timestamp).toLocaleString("zh-CN", {year: "num…...

【雕爷学编程】Arduino动手做(186)---WeMos ESP32开发板8

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…...

STM32--综述

文章目录 前言STM32简介STM32F103C8T6系统结构Keil软件安装注意事项新建工程操作流程 前言 本专栏将学习B站江协科技的STM32入门教程&#xff0c;通过自身理解和对老师的总结所写的博客专栏。 STM32简介 STM32是意法半导体&#xff08;STMicroelectronics&#xff09;公司推…...

Linux学习之sed、awk和vim的差异

sed、awk和vim都是编辑器&#xff0c;区别如下&#xff1a; vim是交互式&#xff0c;需要跟用户进行互动&#xff0c;而sed和awk是非交互式&#xff0c;只需要写好命令&#xff0c;不用跟用户进行互动就可以完成任务。 vim是文本编辑器&#xff0c;操作的时候会对整个文件编辑&…...

MacOS上配置docker国内镜像仓库地址

背景 docker官方镜像仓库网速较差&#xff0c;我们需要设置国内镜像服务 我的MacOS docker版本如下 设置docker国内镜像仓库地址 点击Settings点击Docker Engine修改配置文件&#xff0c;添加registry-mirrors {"builder": {"gc": {"defaultKeepS…...

全志F1C200S嵌入式驱动开发(soc系统集成)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 任何一个嵌入式设备都是由很多的子系统组成的。这里面有硬件、有软件,还可能有机械,并不一定就是大家看到的消费电子那样,即一个soc构成了所有的系统。现实情况是,要构建一个系…...

React路由5版本

什么是路由? 一个路由就是一个映射关系(key:value). 以下代码用的都是router5 通过 npm install react-router-dom5 下载 所有路由用到的东西都需要从react-router-dom中引入 import {BrowserRouter,Link,Route,NavLink,Redirect,withRouter} from react-router-dom 1. 路…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...

云原生周刊:k0s 成为 CNCF 沙箱项目

开源项目推荐 HAMi HAMi&#xff08;原名 k8s‑vGPU‑scheduler&#xff09;是一款 CNCF Sandbox 级别的开源 K8s 中间件&#xff0c;通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度&#xff0c;为容器提供统一接口&#xff0c;实现细粒度资源配额…...