FFmpeg将编码后数据保存成mp4

      以下测试代码实现的功能是:持续从内存块中获取原始数据,然后依次进行解码、编码、最后保存成mp4视频文件。

      可保存成单个视频文件,也可指定每个视频文件的总帧数,保存多个视频文件。

      为了便于查看和修改,这里将可独立的程序段存放在单个函数中:

      1.线程函数set_packet:独立线程,用于用户向指定的内存块中持续写入数据,这里通过调用队列类PacketScaleQueue,可参考:https://blog.csdn.net/fengbingchun/article/details/132128885 中说明。这里填充的数据参考FFmpeg源码中的doc/examples/encode_video.c:每次向队列中写入一帧数据

void set_packet(PacketScaleQueue& packet_encode)
{while (packet_encode_flag) {Buffer buffer;packet_encode.popPacket(buffer);static int i = 0;// refrence: ffmpeg/doc/examples/encode_video.c// prepare a dummy image: Yunsigned char* p1 = buffer.data;for (auto y = 0; y < height; ++y) {for (auto x = 0; x < width; ++x) {p1[y * width + x] = x + y + i * 3;}}// Cb and Crunsigned char* p2 = buffer.data + width * height;unsigned char* p3 = buffer.data + width * height + width * height / 4;for (auto y = 0; y < height / 2; ++y) {for (auto x = 0; x < width / 2; ++x) {p2[y * width / 2 + x] = 128 + y + i * 2;p3[y * width / 2 + x] = 64 + x + i * 5;}}packet_encode.pushScale(buffer);if (++i > 25) i = 0;std::this_thread::sleep_for(std::chrono::milliseconds(40));}
}

      2.回调函数read_packet:avio_alloc_context中使用,不断从队列中获取数据,av_read_frame中也会从这里获取数据:每次从队列中获取一帧数据

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;
}

      3.函数get_input_format_context:创建输入AVFormatContext,需要调用av_dict_set设置video_size和pixel_format

AVFormatContext* get_input_format_context(AVIOContext* avio_ctx)
{AVFormatContext* ifmt_ctx = avformat_alloc_context();if (!ifmt_ctx) {print_error_string(AVERROR(ENOMEM));return nullptr;}ifmt_ctx->pb = avio_ctx;AVDictionary* dict = nullptr;av_dict_set(&dict, "video_size", video_size, 0);av_dict_set(&dict, "pixel_format", pixel_format, 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 nullptr;}av_dict_free(&dict);ret = avformat_find_stream_info(ifmt_ctx, nullptr);if (ret < 0) {fprintf(stderr, "Could not find stream information\n");print_error_string(ret);return nullptr;}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 nullptr;}if (ifmt_ctx->streams[video_stream_index]->codecpar->codec_id != AV_CODEC_ID_RAWVIDEO) {fprintf(stderr, "error: this test code only support rawvideo encode: %d\n", ifmt_ctx->streams[video_stream_index]->codecpar->codec_id);return nullptr;}av_dump_format(ifmt_ctx, 0, "nothing", 0);return ifmt_ctx;
}

      4.函数get_decode_context:创建解码AVCodecContext

AVCodecContext* get_decode_context(AVFormatContext* ifmt_ctx)
{AVCodec* decoder = nullptr;auto ret = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);if (ret < 0) {fprintf(stderr, "fail to av_find_best_stream: %d\n", ret);print_error_string(ret);return nullptr;}AVCodecContext* dec_ctx = avcodec_alloc_context3(decoder);if (!dec_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return nullptr;}ret = avcodec_parameters_to_context(dec_ctx, ifmt_ctx->streams[video_stream_index]->codecpar);if (ret < 0) {fprintf(stderr, "fail to avcodec_parameters_to_context: %d\n", ret);print_error_string(ret);return nullptr;}dec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, ifmt_ctx->streams[video_stream_index], nullptr);ret = avcodec_open2(dec_ctx, decoder, nullptr);if (ret != 0) {fprintf(stderr, "fail to avcodec_open2: %d\n", ret);print_error_string(ret);return nullptr;}return dec_ctx;
}

      5.函数get_encode_context:创建编码AVCodecContext,注意对enc_ctx的相关成员的赋值

AVCodecContext* get_encode_context(AVFormatContext* ifmt_ctx)
{AVCodec* encodec = avcodec_find_encoder_by_name("mpeg1video"); // ffmpeg.exe -encodersif (!encodec) {fprintf(stderr, "fail to avcodec_find_encoder_by_name\n");return nullptr;}AVCodecContext* enc_ctx = avcodec_alloc_context3(encodec);if (!enc_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return nullptr;}enc_ctx->bit_rate = 400000;enc_ctx->framerate = ifmt_ctx->streams[video_stream_index]->r_frame_rate;enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;enc_ctx->height = ifmt_ctx->streams[video_stream_index]->codecpar->height;enc_ctx->width = ifmt_ctx->streams[video_stream_index]->codecpar->width;enc_ctx->time_base = av_inv_q(av_d2q(frame_rate, 4096));enc_ctx->max_b_frames = 1;//if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) // trueenc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;auto ret = avcodec_open2(enc_ctx, encodec, nullptr);if (ret != 0) {fprintf(stderr, "fail to avcodec_open2: %d\n", ret);print_error_string(ret);return nullptr;}return enc_ctx;
}

      6.函数get_output_format_context:创建输出AVFormatContext

AVFormatContext* get_output_format_context(const AVCodecContext* enc_ctx, const char* filename)
{AVFormatContext* ofmt_ctx = nullptr;auto ret = avformat_alloc_output_context2(&ofmt_ctx, nullptr, nullptr, filename);if (ret < 0 || !ofmt_ctx) {fprintf(stderr, "fail to avformat_alloc_output_context2: %d\n", ret);print_error_string(ret);return nullptr;}AVStream* out_stream = avformat_new_stream(ofmt_ctx, nullptr);if (!out_stream) {fprintf(stderr, "fail to avformat_new_stream\n");return nullptr;}ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);if (ret < 0) {fprintf(stderr, "fail to avcodec_parameters_from_context: %d\n", ret);print_error_string(ret);return nullptr;}out_stream->time_base = enc_ctx->time_base;if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) { // trueret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);if (ret < 0) {fprintf(stderr, "fail to avio_open: %d\n", ret);print_error_string(ret);return nullptr;}}ret = avformat_write_header(ofmt_ctx, nullptr);if (ret < 0) {fprintf(stderr, "fail to avformat_write_header: %d\n", ret);print_error_string(ret);return nullptr;}av_dump_format(ofmt_ctx, 0, filename, 1);return ofmt_ctx;
}

      7.函数decode: 注意对packet的判断,它有可能为nullptr;调用一次avcodec_send_packet,可能需调用多次avcodec_receive_frame,因此需要将avcodec_receive_frame放在while中;需要对dec_ctx->time_base进行设置

int decode(AVPacket* packet, AVFormatContext* ifmt_ctx, AVCodecContext* dec_ctx, AVFrame* frame)
{if (packet)av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, dec_ctx->time_base);auto ret = avcodec_send_packet(dec_ctx, packet);if (ret < 0) {fprintf(stderr, "fail to avcodec_send_packet: %d\n", ret);print_error_string(ret);return -1;}while (1) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): decode is not yet complete//fprintf(stderr, "Warning: avcodec_receive_frame: %d\n", ret);//print_error_string(ret);break;}else if (ret < 0) {fprintf(stderr, "fail to avcodec_receive_frame: %d\n", ret);print_error_string(ret);return ret;}frame->pts = frame->best_effort_timestamp;break;}if (packet)av_packet_unref(packet);return 0;
}

      8.函数encode:注意:调用一次avcodec_send_frame,可能需要调用多次avcodec_receive_packet,因此需要将avcodec_receive_packet放在while中;需要对ofmt_ctx->streams[0]->time_base进行设置,否则生成的mp4中无法获取帧速率;也可在调用avformat_write_header时设置video_track_timescale指定

int encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* packet, AVFormatContext* ifmt_ctx, AVFormatContext* ofmt_ctx)
{auto ret = avcodec_send_frame(enc_ctx, frame);if (ret < 0) {if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {fprintf(stderr, "Warning: avcodec_send_frame: %d\n", ret);print_error_string(ret);ret = 0;}else {fprintf(stderr, "fail to avcodec_send_frame: %d\n", ret);print_error_string(ret);return ret;}}while (1) {ret = avcodec_receive_packet(enc_ctx, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { // AVERROR(EAGAIN): encode is not yet complete//fprintf(stderr, "Warning: avcodec_receive_packet: %d\n", ret);//print_error_string(ret);break;}if (ret < 0) {fprintf(stderr, "fail to avcodec_receive_packet: %d\n", ret);print_error_string(ret);return ret;}packet->stream_index = 0;av_packet_rescale_ts(packet, ifmt_ctx->streams[video_stream_index]->time_base, ofmt_ctx->streams[0]->time_base);//packet2->pts = packet2->dts = frame->pts *//    ofmt_ctx->streams[0]->time_base.den / ofmt_ctx->streams[0]->time_base.num / //    (enc_ctx->framerate.num / enc_ctx->framerate.den);ret = av_interleaved_write_frame(ofmt_ctx, packet);if (ret < 0) {print_error_string(ret);return ret;}av_packet_unref(packet);}return 0;
}

      9.主函数test_ffmpeg_save_video_slice:注意:在退出前需要flush decoder和encoder;写入视频文件需先调用avformat_write_header,然后持续调用av_interleaved_write_frame,退出前再需调用av_write_trailer;每次新文件的写入,需重新调用get_decode_context、get_encode_context、get_output_format_context并在之前作相应free

int test_ffmpeg_save_video_slice()
{PacketScaleQueue packet_encode;packet_encode.init(queue_size, 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 = get_input_format_context(avio_ctx);if (!ifmt_ctx) {fprintf(stderr, "fail to get_input_format_context\n");return -1;}AVPacket *packet = av_packet_alloc(), *packet2 = av_packet_alloc();if (!packet || !packet2) {fprintf(stderr, "fail to av_packet_alloc\n");return -1;}AVFrame* frame = av_frame_alloc();if (!frame) {fprintf(stderr, "fail to av_frame_alloc\n");return -1;}int count = 0, name = 1;AVCodecContext* enc_ctx = nullptr;AVFormatContext* ofmt_ctx = nullptr;AVCodecContext* dec_ctx = nullptr;while (1) {auto ret = av_read_frame(ifmt_ctx, packet);if (ret < 0) {break;}if (packet->stream_index != video_stream_index) {av_packet_unref(packet);continue;}if (count % slice_size == 0) {enc_ctx = get_encode_context(ifmt_ctx);if (!enc_ctx) {fprintf(stderr, "fail to avcodec_alloc_context3\n");return -1;}std::string filename = std::to_string(name) + ".mp4";filename = std::string(path) + filename;ofmt_ctx = get_output_format_context(enc_ctx, filename.c_str());if (!ofmt_ctx) {fprintf(stderr, "fail to get_output_format_context\n");return -1;}dec_ctx = get_decode_context(ifmt_ctx);if (!dec_ctx) {fprintf(stderr, "fail to get_decode_context\n");return -1;}++name;}if (decode(packet, ifmt_ctx, dec_ctx, frame) != 0) return -1;if (encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx) != 0) return -1;//fprintf(stdout, "count: %d\n", count);if (count + 1 == total_frames) { // terminate looppacket_encode_flag = false;// flush the decoderdecode(nullptr, ifmt_ctx, dec_ctx, frame);if (frame->data[0])encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);// flush the encoderencode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);av_write_trailer(ofmt_ctx);break;}++count;if (count > 1 && count % slice_size == 0) {// flush the decoderdecode(nullptr, ifmt_ctx, dec_ctx, frame);if (frame->data[0])encode(enc_ctx, frame, packet2, ifmt_ctx, ofmt_ctx);// flush the encoderencode(enc_ctx, nullptr, packet2, ifmt_ctx, ofmt_ctx);av_write_trailer(ofmt_ctx);avcodec_free_context(&dec_ctx);avcodec_free_context(&enc_ctx);avio_closep(&ofmt_ctx->pb);avformat_free_context(ofmt_ctx);}}av_packet_free(&packet);av_packet_free(&packet2);av_frame_free(&frame);avcodec_free_context(&dec_ctx);avcodec_free_context(&enc_ctx);avio_closep(&ofmt_ctx->pb);avformat_close_input(&ofmt_ctx);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);}thread_packet.join();fprintf(stdout, "test finish\n");return 0;
}

      执行结果如下图所示:

      可调用ffprobe.exe验证每个生成的视频文件的总帧数,如1.mp4,执行如下命令:

ffprobe.exe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=nokey=1:noprint_wrappers=1 1.mp4

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[CKA]考试之检查可用节点数量

由于最新的CKA考试改版&#xff0c;不允许存储书签&#xff0c;本博客致力怎么一步步从官网把答案找到&#xff0c;如何修改把题做对&#xff0c;下面开始我们的 CKA之旅 题目为&#xff1a; Task 检查集群中有多少节点为Ready状态&#xff08;不包括被打上 Taint&#xff1…

FPGA学习——Altera IP核调用之PLL篇

文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核&#xff08;知识产权核&#xff09;&#xff0c;是在集成电路的可…

视频监控汇聚平台EasyCVR视频分享页面WebRTC流地址播放不了是什么原因?

开源EasyDarwin视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多…

机器学习笔记之优化算法(八)简单认识Wolfe Condition的收敛性证明

机器学习笔记之优化算法——简单认识Wolfe Condition收敛性证明 引言回顾&#xff1a; Wolfe \text{Wolfe} Wolfe准则准备工作推导条件介绍推导结论介绍 关于 Wolfe \text{Wolfe} Wolfe准则收敛性证明的推导过程 引言 上一节介绍了非精确搜索方法—— Wolfe \text{Wolfe} Wolf…

【Python】从同步到异步多核:测试桩性能优化,加速应用的开发和验证

目录 测试工作中常用到的测试桩mock能力 应用场景 简单测试桩 http.server扩展&#xff1a;一行命令实现一个静态文件服务器 性能优化&#xff1a;使用异步响应 异步响应 能优化&#xff1a;利用多核 gunicorn 安装 gunicorn 使用 gunicorn 启动服务 性能优化&#…

linuxARM裸机学习笔记(2)----汇编LED灯实验

MX6ULL 的 IO IO的复用功能 这里的只使用了低五位&#xff0c;用来配置io口&#xff0c;其中bit0~bit3(MUX_MODE)就是设置 GPIO1_IO00 的复用功能的&#xff0c;GPIO1_IO00 一共可以复用为 9种功能 IO&#xff0c;分别对应 ALT0~ALT8。每种对应了不同的功能 io的属性配置 HY…

MATLAB(R2023a)添加工具箱TooLbox的方法-以GPOPS为例

一、找到工具箱存放位置 首先我们需要找到工具箱的存放位置&#xff0c;点击这个设置路径可以看到 我们的matlab工具箱的存放位置 C:\Program Files\MATLAB\R2023a\toolbox\matlab 从资源管理器中打开这个位置&#xff0c;可以看到里面各种工具箱 二、放入工具箱 解压我们…

【docker】docker私有仓库

目录 一、说明二、私有仓库搭建三、上传镜像到私有仓库四、从私有仓库拉取镜像 一、说明 1.docker官方的docker hub(https://hub.docker.com)是一个用于管理公共镜像的仓库&#xff0c;可以从上面拉取镜像到本地&#xff0c;也可以把自己的镜像推送上去 2.若服务器无法访问互联…

FreeRTOS(vTaskList与vTaskGetRunTimeStats)

目录 1、Cube配置 ①配置SYS ②配置TIM3 ③配置USART2 ④配置FreeRTOS ⑤配置中断优先级 2、代码添加改动 ①在main函数合适位置开启TIM3中断 ②修改HAL_TIM_PeriodElapsedCallback函数 ③完善两个相关函数 ④vTaskList与vTaskGetRunTimeStats的使用 vTaskList&#xff…

关于多媒体视频翻译,你了解多少?

近年来&#xff0c;随着多媒体视频行业的快速发展&#xff0c;观看中外视频已成为数千万人的日常习惯&#xff0c;进而促进了视频翻译需求量的不断增加。那么&#xff0c;如何做好多媒体视频翻译服务&#xff0c;关于多媒体视频翻译&#xff0c;你了解多少&#xff1f; 据了解&…

day5 6 7-牛客67道剑指offer-JZ43、45、49、50、51、52、53、55、79、数组中只出现一次的数字

文章目录 1. JZ43 整数中1出现的次数&#xff08;从1到n整数中1出现的次数&#xff09;2. JZ45 把数组排成最小的数3. JZ49 丑数最小堆三指针法 动态规划 4. JZ50 第一个只出现一次的字符5. JZ51 数组中的逆序对6. JZ52 两个链表的第一个公共结点迭代递归 7. JZ53 数字在升序数…

当编程式事务写在了声明式事务的代码中,会怎么样?

1、背景 上一篇文章&#xff0c;为解决大事务问题&#xff0c;将for循环的每个订单的处理放在了编程式事务的代码中&#xff0c;但是在落地的时候&#xff0c;遇到了一个比较特殊的情况&#xff0c;外层方法中使用了 Transactional注解&#xff0c;内部再用编程式事务&#xf…

大数据技术之Hadoop(二)

目录 一、Hadoop的诞生 二、大数据概述 三、大数据软件生态 3.1 数据存储相关技术 3.2 数据计算相关技术 3.3 数据传输相关技术 四、什么是Hadoop 本篇主要讲解大数据的核心概念以及Hadoop的基本介绍。 一、Hadoop的诞生 大数据的发展与日益庞大的数据量是密不可分的。从…

chaitin-Nginx+Docker

Nginx实战 任务一 1、源码包安装NGINX A&#xff0c;搭建Web Server&#xff0c;任意HTML页面&#xff0c;其8080端口提供Web访问服务&#xff0c;截图成功访问http(s)&#x1f615;/[Server1]:8080并且回显Web页面 官网地址&#xff1a;http://nginx.org/en/download.html 步骤…

webpack基础知识十:与webpack类似的工具还有哪些?区别?

一、模块化工具 模块化是一种处理复杂系统分解为更好的可管理模块的方式 可以用来分割&#xff0c;组织和打包应用。每个模块完成一个特定的子功能&#xff0c;所有的模块按某种方法组装起来&#xff0c;成为一个整体(bundle) 在前端领域中&#xff0c;并非只有webpack这一款…

力扣初级算法(二分查找)

力扣初级算法(二分法)&#xff1a; 每日一算法&#xff1a;二分法查找 学习内容&#xff1a; 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 2.二分查找流程&…

深度学习——划分自定义数据集

深度学习——划分自定义数据集 以人脸表情数据集raf_db为例&#xff0c;初始目录如下&#xff1a; 需要经过处理后返回 train_images, train_label, val_images, val_label 定义 read_split_data(root: str, val_rate: float 0.2) 方法来解决&#xff0c;代码如下&#xff1a…

[openCV]基于拟合中线的智能车巡线方案V3

import cv2 as cv import os import numpy as np# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir&#xff1a;文件夹根目录输入 ext: 扩展名返回&#xff1a; 文件路径列表"""newDir d…

SpringBoot 自动配置--常用配置

&#x1f600;前言 本篇博文是关于SpringBoot 自动配置的一些分享&#xff0c;希望能够帮助到您&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您…

关于安卓jar包修改并且重新发布

背景&#xff1a; 对于某些jar包&#xff0c;其内部是存在bug的&#xff0c;解决的方法无外乎就有以下几种方法&#xff1a; &#xff08;1&#xff09;通过反射&#xff0c;修改其赋值逻辑 &#xff08;2&#xff09;通过继承&#xff0c;重写其方法 &#xff08;3&#xff0…