ffmpeg 库yuv420转jpeg(内存)

2024-04-08 11:38
文章标签 内存 ffmpeg jpeg yuv420

本文主要是介绍ffmpeg 库yuv420转jpeg(内存),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ffmpeg 库yuv420转jpeg(内存)

近来实现mjpeg的http实时流,需要yuv420转jpeg。了解了一下主要可以通过ffmpeg或者libjpeg(terbo)实现。
现想用ffmpeg,网上搜了一下,资料不多。比较热门的是雷神的博客:
http://blog.csdn.net/leixiaohua1020/article/details/25346147
但有个问题,例子是写文件的,使用到AVFormatContext,封装好的读写,没法从内存里搞出来。
自己对ffmpeg也是小白一个,只能慢慢查找资料研究。解决方法是自己定义AVCodecContext。

pCodecCtx = avcodec_alloc_context3(pCodec);

这样总算能拿到编码后的buff了。但还有一个问题,在调试雷神的demo的时候发现编码后的图片明显有颜色的差别。
编码前编码后

一开始以为是质量的问题,找了很久也没发现哪里可以调质量(有人知道请告诉我一声)
后来一想不太对,ffmpeg中jpeg编码输入要求YUVJ420P。但YUVJ420PYUV420P是不一样的,他们的range有大有小。参考https://en.wikipedia.org/wiki/YUV
雷神的例子中直接把YUV420P当成YUVJ420P编码可能存在颜色出错。所以解决方法就是
1.YUV420P to YUVJ420P
2.YUVJ420P to jpeg

最终加上YUV420P 转 YUVJ420P的代码就可以了。
完整的代码如下,可下载雷神的demo修改:

extern "C"
{
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"  
};
int main(int argc, char* argv[])
{AVCodec *pCodec;AVCodecContext *pCodecCtx = NULL;int i, ret, got_output;FILE *fp_in;FILE *fp_out;AVFrame *pFrame;AVPacket pkt;int y_size;int framecnt = 0;struct SwsContext *img_convert_ctx;AVFrame *pFrame2;char filename_in[] = "cuc_view_480x272.yuv";AVCodecID codec_id = AV_CODEC_ID_MJPEG;char filename_out[] = "mytest.jpg";int in_w = 480, in_h = 272;int framenum = 1;avcodec_register_all();pCodec = avcodec_find_encoder(codec_id);if (!pCodec) {printf("Codec not found\n");return -1;}pCodecCtx = avcodec_alloc_context3(pCodec);if (!pCodecCtx) {printf("Could not allocate video codec context\n");return -1;}pCodecCtx->bit_rate = 4000000;pCodecCtx->width = in_w;pCodecCtx->height = in_h;pCodecCtx->time_base.num = 1;pCodecCtx->time_base.den = 11;pCodecCtx->gop_size = 75;//pCodecCtx->max_b_frames = 0;//pCodecCtx->global_quality = 1;pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;//if (codec_id == AV_CODEC_ID_H264)//  av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {printf("Could not open codec\n");return -1;}pFrame = av_frame_alloc();if (!pFrame) {printf("Could not allocate video frame\n");return -1;}pFrame->format = pCodecCtx->pix_fmt;pFrame->width = pCodecCtx->width;pFrame->height = pCodecCtx->height;ret = av_image_alloc(pFrame->data, pFrame->linesize, pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, 16);if (ret < 0) {printf("Could not allocate raw picture buffer\n");return -1;}pFrame2 = av_frame_alloc();if (!pFrame) {printf("Could not allocate video frame\n");return -1;}pFrame2->format = AV_PIX_FMT_YUV420P;pFrame2->width = pCodecCtx->width;pFrame2->height = pCodecCtx->height;ret = av_image_alloc(pFrame2->data, pFrame2->linesize, pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_YUV420P, 16);if (ret < 0) {printf("Could not allocate raw picture buffer\n");return -1;}img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUVJ420P, SWS_BICUBIC, NULL, NULL, NULL);//Input raw datafp_in = fopen(filename_in, "rb");if (!fp_in) {printf("Could not open %s\n", filename_in);return -1;}//Output bitstreamfp_out = fopen(filename_out, "wb");if (!fp_out) {printf("Could not open %s\n", filename_out);return -1;}y_size = pCodecCtx->width * pCodecCtx->height;//Encodefor (i = 0; i < framenum; i++) {av_init_packet(&pkt);pkt.data = NULL;    // packet data will be allocated by the encoderpkt.size = 0;//Read raw YUV dataif (fread(pFrame2->data[0], 1, y_size, fp_in) <= 0 ||       // Yfread(pFrame2->data[1], 1, y_size / 4, fp_in) <= 0 ||   // Ufread(pFrame2->data[2], 1, y_size / 4, fp_in) <= 0) {   // Vreturn -1;}else if (feof(fp_in)) {break;}sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame2->data, pFrame2->linesize, 0, pCodecCtx->height, pFrame->data, pFrame->linesize);pFrame->pts = i;/* encode the image */ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_output);if (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);framecnt++;fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}//Flush Encoderfor (got_output = 1; got_output; i++) {ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_output);if (ret < 0) {printf("Error encoding frame\n");return -1;}if (got_output) {printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n", pkt.size);fwrite(pkt.data, 1, pkt.size, fp_out);av_free_packet(&pkt);}}fclose(fp_out);avcodec_close(pCodecCtx);av_free(pCodecCtx);av_freep(&pFrame->data[0]);av_frame_free(&pFrame);av_frame_free(&pFrame2);return 0;
}

附:yuv颜色range转换原理

一开始我是想自己查查怎么转换range的。找了很久没找到什么例子,最后使用ffmpeg转换了。有时间得补补图像基础。
实际上我猜想是等比变换的,应该不会太复杂,我看了一下内存里面几个值对比了一下也比较符合。有时间看看ffmpeg的源码验证一下。
y的range是 [16,235] to [0,255]
u、v的range是 [16,240] to [0,255]

所以y的等式如下:
y420-16 / 235-16 = yj420 / 255
uv的如下:
uv420-16 / 240-16 = uvj420 / 255
根据以上求解转换。

从来没这么认真写过博客,感觉耗时甚多。不知道那些写很多博客的大神,还写的很好的是怎么做到的。

这篇关于ffmpeg 库yuv420转jpeg(内存)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/885488

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Linux的ffmpeg python的关键帧抽取

《基于Linux的ffmpegpython的关键帧抽取》本文主要介绍了基于Linux的ffmpegpython的关键帧抽取,实现以按帧或时间间隔抽取关键帧,文中通过示例代码介绍的非常详细,对大家的学... 目录1.FFmpeg的环境配置1) 创建一个虚拟环境envjavascript2) ffmpeg-py

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Redis过期删除机制与内存淘汰策略的解析指南

《Redis过期删除机制与内存淘汰策略的解析指南》在使用Redis构建缓存系统时,很多开发者只设置了EXPIRE但却忽略了背后Redis的过期删除机制与内存淘汰策略,下面小编就来和大家详细介绍一下... 目录1、简述2、Redis http://www.chinasem.cn的过期删除策略(Key Expir

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

SpringBoot使用ffmpeg实现视频压缩

《SpringBoot使用ffmpeg实现视频压缩》FFmpeg是一个开源的跨平台多媒体处理工具集,用于录制,转换,编辑和流式传输音频和视频,本文将使用ffmpeg实现视频压缩功能,有需要的可以参考... 目录核心功能1.格式转换2.编解码3.音视频处理4.流媒体支持5.滤镜(Filter)安装配置linu

Java内存区域与内存溢出异常的详细探讨

《Java内存区域与内存溢出异常的详细探讨》:本文主要介绍Java内存区域与内存溢出异常的相关资料,分析异常原因并提供解决策略,如参数调整、代码优化等,帮助开发者排查内存问题,需要的朋友可以参考下... 目录一、引言二、Java 运行时数据区域(一)程序计数器(二)Java 虚拟机栈(三)本地方法栈(四)J

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据