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_buffer
    if (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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/65687.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《孙子兵法》快速概览,有哪些章节?趣讲《孙子兵法》【第2讲】

《孙子兵法》快速概览&#xff0c;有哪些章节&#xff1f;趣讲《孙子兵法》【第2讲】 《孙子兵法》十一家注是一个有名的版本&#xff0c;十一家注是曹操、杜牧等十一人注释&#xff0c;曹操是真正的军事家&#xff0c;是名副其实的大咖。总共三卷十三篇&#xff0c;比较难记住…

3个月快速入门LoRa物联网传感器开发

在这里插入图片描述 快速入门LoRa物联网传感器开发 LoRa作为一种LPWAN(低功耗广域网络)无线通信技术,非常适合物联网传感器和行业应用。要快速掌握LoRa开发,需要系统学习理论知识,并通过实际项目积累经验。 摘要: 先学习LoRa基础知识:原理、网络架构、协议等,大概需要2周时间…

Java加密算法的应用与实现(MD5、SHA、DES、3DES、AES、RSA、ECC)

文章目录 一、散列加密算法1、概述2、常见算法&#xff08;MD5、SHA&#xff09;3、应用4、Java实现 二、对称加密算法1、概述2、常见算法&#xff08;DES、3DES、AES&#xff09;3、应用4、Java实现AES 三、非对称加密算法1、概述2、常见算法&#xff08;RSA、ElGamal、Rabin、…

【linux】ssh 和adb connect区别

问&#xff1a;ssh 与ping的区别 答&#xff1a;SSH&#xff08;Secure Shell&#xff09;和Ping是两种完全不同的网络工具。 SSH是一种加密的网络协议&#xff0c;用于安全地远程管理或访问远程计算机。它提供了一种安全的通信方式&#xff0c;可以在不安全的网络上进行远程登…

淘宝API接口为开发者提供了与淘宝平台进行数据交互和操作的便捷途径

淘宝API接口是指淘宝开放平台提供的一套接口&#xff0c;用于与淘宝网进行数据交互和操作。通过使用淘宝API接口&#xff0c;第三方开发者可以实现商品搜索、店铺信息获取、订单管理、商家服务等功能&#xff0c;从而实现与淘宝平台的对接和数据共享。 淘宝API接口的使用可以帮…

备忘录模式(Memento)

备忘录模式是一种行为设计模式&#xff0c;在不破坏封装性的前提下&#xff0c;允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。 Memento is a behavior design pattern. Without compromising encapsulation, it can reserve and restore of the previous stat…

vscode自动添加注释说明

1. 安装vscode 双击安装程序,默认安装即可(如:VSCodeSetup-x64-1.70.2.exe) 2. 安装doxygen文档生成插件 1> 打开vscode软件,点击左侧插件管理菜单 2> 点击右上角’…‘按钮,选择’Install from VSIX’(联网状态可以直接搜索doxygen下载安装) 3> 选择doxygen离线安装…

Java课题笔记~ 使用 Spring 的事务注解管理事务(掌握)

通过Transactional 注解方式&#xff0c;可将事务织入到相应 public 方法中&#xff0c;实现事务管理。 Transactional 的所有可选属性如下所示&#xff1a; propagation&#xff1a;用于设置事务传播属性。该属性类型为 Propagation 枚举&#xff0c; 默认值为 Propagation.R…

【SpringBoot】日志是什么+基于lombok的日志输出

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE进阶 在我们日常的程序开发中&#xff0c;日志是程序的重要组成部分&#xff0c;想象⼀下&#xff0c;如果程序报错了&#xff0c;不让你打开控制台看⽇志&#xff0c;那么你能找到报错的原因吗…

BEM命名规范

参加了一个团队开发的小项目&#xff0c;代码写完了一看别人的感觉自己写的老不规范了&#xff0c;后知后觉才看到开发文档里面的样式书写规范。感觉要大改了……也算给自己长个记性要先读完所有文档在开始。 也学习了解了一下BEM命名规范。 1. 什么是BEM&#xff1f; BEM&a…

软件测试(功能、接口、性能、自动化)详解

一、软件测试功能测试 测试用例编写是软件测试的基本技能&#xff1b;也有很多人认为测试用例是软件测试的核心&#xff1b;软件测试中最重要的是设计和生成有效的测试用例&#xff1b;测试用例是测试工作的指导&#xff0c;是软件测试的必须遵守的准则。 黑盒测试常见测试用…

MinIO:微服务中上传图片流程

1、在nacos中配置minio参数 2、controller层 package com.heima.wemedia.controller.v1;import com.heima.model.common.dtos.ResponseResult; import com.heima.wemedia.service.WmMaterialService; import org.springframework.beans.factory.annotation.Autowired; import …

用栈判断是否匹配

1 问题 写代码的时候用到的括号都是成双成对的出现&#xff0c;并且大小也相同。在集成编辑环境中&#xff0c;IDE就会为我们自己动检查括号是否匹配。那么为了避免在报错&#xff0c;如何判断是否有无括号不匹配&#xff1f; 2 方法 利用栈来实现这种功能。当遇见一个左括号&a…

SpringCloud-Hystrix服务熔断与降级工作原理源码 | 京东物流技术团队

先附上Hystrix源码图 在微服务架构中&#xff0c;根据业务来拆分成一个个的服务&#xff0c;服务与服务之间可以相互调用&#xff08;RPC&#xff09;&#xff0c;在Spring Cloud可以用RestTemplateRibbon和Feign来调用。为了保证其高可用&#xff0c;单个服务通常会集群部署。…

代码随想录算法训练营day59

文章目录 Day59 下一个更大元素II题目思路代码 接雨水题目思路代码 Day59 下一个更大元素II 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 题目 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每…

UNIX网络编程卷一 学习笔记 第二十七章 IP选项

IPv4允许在20字节的首部固定部分后跟最多共40字节的选项。尽管已经定义了10种IPv4选项&#xff0c;但最常用的是源路径选项。我们可通过存取IP_OPTIONS套接字选项访问这些选项&#xff0c;我们存取该套接字选项时&#xff0c;所用的缓冲区中的值就是它们置于IP数据报中的格式。…

第二课-一键安装SD-Stable Diffusion 教程

前言 看完这篇文章并跟着操作,就可以在本地开始 SD 绘图了。 理论上来说,这篇课程结束,想要画什么图都可以画了。 启动器介绍 SD 是开源的,可以在 github 上找到。但直接下载源码安装,非常费劲,而且因为国内外差异,就是我这样的秃头程序员也难以应对。 所以,我们改…

学会RabbitMQ的延迟队列,提高消息处理效率

系列文章目录 手把手教你&#xff0c;本地RabbitMQ服务搭建&#xff08;windows&#xff09; 消息队列选型——为什么选择RabbitMQ RabbitMQ灵活运用&#xff0c;怎么理解五种消息模型 RabbitMQ 能保证消息可靠性吗 推或拉&#xff1f; RabbitMQ 消费模式该如何选择 死信是什么…

滑动窗口(全面清晰/Java)

数组模拟单调队列 分析 以k3举例&#xff1a; (1)利用单调队列的性质&#xff1a; <1>最小值&#xff1a;确保队列单调递增&#xff0c;处理后&#xff0c;队头即是最小值。 <2>最大值&#xff1a;确保队列单调递减&#xff0c;处理后&#xff0c;队头即是最大值…

分享一个计算器

先看效果&#xff1a; 再看代码&#xff08;查看更多&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>计算器</title><style>* {box-sizing: border-box;}body…