FFMPEG内存操作(二)从内存中读取数及数据格式的转换
相关博客列表:
FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析
在雷神的《最简单的基于FFmpeg的内存读写例子(内存播放器)》中,它是设计回调函数从输入文件中读取数据。与FFMPEG 官方给出的avio_reading.c不同的是,雷神给的例子是当需要数据的时候,回调函数才去从输入文件读取数据,而avio_reading.c 则是直接全部数据读取到内存中待后面处理。
在我的这个实例中,我是将读取到的输入文件解码成YUV420P数据格式。同时可以通过设置不同的数据格式和尺寸实现输出图像的拉伸缩小或是数据格式的装换。
- /*=============================================================================
- # FileName: ffmpeg_mem_read.c
- # Desc: an example of ffmpeg read from memory
- # Author: licaibiao
- # LastChange: 2017-03-21
- =============================================================================*/
- #include <stdio.h>
- #define __STDC_CONSTANT_MACROS
- #include "avcodec.h"
- #include "avformat.h"
- #include "swscale.h"
- //#define QUARTER_SHOW
- charchar *input_name = "cuc60anniversary_start.ts";
- FILEFILE *fp_open = NULL;
- int read_buffer(voidvoid *opaque, uint8_t *buf, int buf_size){
- if(!feof(fp_open)){
- int true_size=fread(buf,1,buf_size,fp_open);
- return true_size;
- }else{
- return -1;
- }
- }
- int main(int argc, char* argv[])
- {
- AVFormatContext *pFormatCtx;
- AVCodecContext *pCodecCtx;
- AVCodec *pCodec;
- AVIOContext *avio;
- AVFrame *pFrame;
- AVFrame *pFrameYUV;
- AVPacket *packet;
- struct SwsContext *img_convert_ctx;
- int videoindex;
- int i;
- int ret;
- int got_picture;
- int y_size;
- unsigned char *aviobuffer;
- FILE *fp_yuv;
- //fp_open = fopen(argv[1],"rb+");
- fp_open = fopen(input_name, "rb+");
- fp_yuv = fopen("output.yuv", "wb+");
- av_register_all();
- pFormatCtx = avformat_alloc_context();
- aviobuffer=(unsigned charchar *)av_malloc(32768);
- avio = avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);
- /* Open an input stream and read the header. */
- pFormatCtx->pb = avio;
- if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){
- printf("Couldn't open input stream.\n");
- return -1;
- }
- /* Read packets of a media file to get stream information. */
- if(avformat_find_stream_info(pFormatCtx,NULL)<0){
- printf("Couldn't find stream information.\n");
- return -1;
- }
- videoindex = -1;
- for(i=0; i<pFormatCtx->nb_streams; i++) {
- if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
- videoindex=i;
- break;
- }
- }
- if(videoindex==-1){
- printf("Didn't find a video stream.\n");
- return -1;
- }
- av_dump_format(pFormatCtx, 0, input_name, 0);
- /* Find a registered decoder with a matching codec ID */
- pCodecCtx = pFormatCtx->streams[videoindex]->codec;
- pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
- if(pCodec==NULL){
- printf("Codec not found.\n");
- return -1;
- }
- /* Initialize the AVCodecContext to use the given AVCodec */
- if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
- printf("Could not open codec.\n");
- return -1;
- }
- pFrame = av_frame_alloc();
- pFrameYUV = av_frame_alloc();
- uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
- avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
- /* Allocate and return an SwsContext. */
- /* srcW:源图像的宽
- * srcH:源图像的高
- * srcFormat:源图像的像素格式
- * dstW:目标图像的宽
- * dstH:目标图像的高
- * dstFormat:目标图像的像素格式
- * flags:设定图像拉伸使用的算法
- */
- #ifndef QUARTER_SHOW
- img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
- #else
- img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width/2, pCodecCtx->height/2, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
- printf("out frame width = %d, height = %d \n", pCodecCtx->width/2,pCodecCtx->height/2);
- #endif
- packet = (AVPacket *)av_malloc(sizeof(AVPacket));
- while(av_read_frame(pFormatCtx, packet) >= 0){
- if(packet->stream_index == videoindex){
- ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
- if(ret < 0){
- printf("Decode Error.\n");
- return -1;
- }
- if(got_picture){
- /* Scale the image slice in srcSlice and put the resulting scaled slice in the image in dst. 图像处理函数 */
- /* c the scaling context previously created with sws_getContext()
- * srcSlice the array containing the pointers to the planes of the source slice
- * srcStride the array containing the strides for each plane of
- * srcSliceY the position in the source image of the slice to process, that is the number (counted starting from
- zero) in the image of the first row of the slice 输入图像数据的第多少列开始逐行扫描,通常设为0
- * srcSliceH the height of the source slice, that is the number of rows in the slice 为需要扫描多少行,通常为输入图像数据的高度
- * dst the array containing the pointers to the planes of the destination image
- * dstStride the array containing the strides for each plane of the destination image
- */
- sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
- #ifndef QUARTER_SHOW
- // printf("pFrameYUV->height = %d\n ",pFrameYUV->height);
- // printf("pFrameYUV->width = %d\n ", pFrameYUV->width);
- // printf("pFrameYUV->pkt_size = %d\n ",pFrameYUV->pkt_size);
- y_size = pCodecCtx->width*pCodecCtx->height;
- fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
- fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
- fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
- #else
- for(i=0; i<pCodecCtx->height/2; i++){
- fwrite(pFrameYUV->data[0]+pCodecCtx->width * i ,1,pCodecCtx->width/2,fp_yuv);
- }
- for(i=0; i<pCodecCtx->height/2; i = i + 2){
- fwrite(pFrameYUV->data[1]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
- }
- for(i=0; i<pCodecCtx->height/2; i = i + 2 ){
- fwrite(pFrameYUV->data[2]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
- }
- #endif
- }
- }
- av_free_packet(packet);
- }
- sws_freeContext(img_convert_ctx);
- fclose(fp_yuv);
- fclose(fp_open);
- av_free(out_buffer);
- av_free(pFrameYUV);
- avcodec_close(pCodecCtx);
- avformat_close_input(&pFormatCtx);
- return 0;
- }
正常操作是将输入文件解码成YUV数据。
当我们对sws_getContext 设置不同参数的时候,我们可以得到不同的输出数据。在这里需要注意几点:
(1)out_buffer 的大小要跟着sws_getContext 参数的设置而改变,如果out_buffer分配得比输出数据小,会出现内存溢出问题。
(2)sws_scale 函数中,我们一般设置从输入数据的第0行开始扫描,扫描的高度(也就是行数)一般是输入数据的高度。
(3)得到的YUV数据是储存在pFrameYUV->data 的三个分量里,需要分开读取。
举例:
在sws_getContext 中设置输出格式的宽和高都是输入格式的一半。正常的显示应该是图像变为了原来的1/4大小。如果我们还是按原图尺寸提取输出数据:
- y_size = pCodecCtx->width*pCodecCtx->height;
- fwrite(pFrameYUV->data[0],1,y_size,fp_yuv); //Y
- fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv); //U
- fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv); //V
得到的图像将会是:
如上图,图像显示是正常的,但同时也提取了很多的无用数据,实际图像并没有缩小到原来的1/4
所以我们需要自己对输出数据再重组:
- for(i=0; i<pCodecCtx->height/2; i++){
- fwrite(pFrameYUV->data[0]+pCodecCtx->width * i ,1,pCodecCtx->width/2,fp_yuv);
- }
- for(i=0; i<pCodecCtx->height/2; i = i + 2){
- fwrite(pFrameYUV->data[1]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
- }
- for(i=0; i<pCodecCtx->height/2; i = i + 2 ){
- fwrite(pFrameYUV->data[2]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
- }
分别重新提取了Y U V 三个分量上的数据,得到图像如下:
在读取输出数据的时候,如果发现读取的输出数据显示有问题,最好是使用UltraEdit 工具对某帧数据进行分析,先确认输出的一帧数据是否正常,然后才进行视频格式的转换。比如出现下面的这种情况
从图像中大概能分析出来,Y分量数据是正常的,图像右半边的UV分量可能丢失。使用UltraEdit查看原始数据,如下图。我们可以看到从地址0xe150开始有一段是全0的数据,因此我们可以到我们的程序中去检查是否UV分量读取错误。
总结:我们直接使用sws_getContext和sws_scale 可以直接对图像进行拉伸和缩放,同时可以进行数据格式的转换,只需要设置sws_getContext 就可以了,非常简单。但是需要注意,输出的数据需要我们自己重新组合,否则读取到的输出数据很有可能出错。
from:http://blog.csdn.net/li_wen01/article/details/64904586
FFMPEG内存操作(二)从内存中读取数及数据格式的转换的更多相关文章
- jvm大局观之内存管理篇(二):当java中new一个对象,背后发生了什么
https://zhuanlan.zhihu.com/p/257863129?utm_source=ZHShareTargetIDMore 番茄番茄我是西瓜 那是我日夜思念深深爱着的人啊~ 已关注 ...
- c# 外挂操作(内存操作)(内存读写取窗口句柄移动窗口取模块地址)工具类
来源于网上 参考 https://www.cnblogs.com/fuhua/p/5877781.html 等众多文章 详情取看我第二个例子封装功能较多 https://www.cnblogs.co ...
- openCV(二)---iOS中使用openCV的图片格式转换
可以实现将UIImage和IplImage类型实现相互转换 //由于OpenCV主要针对的是计算机视觉方面的处理,因此在函数库中,最重要的结构体是IplImage结构. - (IplImage *)C ...
- python从excel中读取数据传给其他函数使用
首先安装xlrd库 pip install xlrd 方法1: 表格内容如下: 场景描述,读取该表格A列数据,然后打印出数据 代码何解析如下: import xlrd #引入xlrd库 def exc ...
- FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析
相关博客列表 : FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析 FFMPEG内存操作(二)从内存中读取数及数据格式的转换 FFmpeg内存操作(三)内存转码器 在F ...
- FFmpeg内存操作(三)内存转码器
相关博客列表 : FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析 FFMPEG内存操作(二)从内存中读取数及数据格式的转换 FFmpeg内存操作(三)内存转码器 本文 ...
- 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用
欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...
- java 如何从配置文件(.properties)中读取内容
1.如何创建.properties文件 很简单,建立一个txt文件,并把后缀改成.properties即可 2.将.properties文件拷入src的根目录下 3..properties文件内容格式 ...
- php中读取文件内容的几种方法。(file_get_contents:将文件内容读入一个字符串)
php中读取文件内容的几种方法.(file_get_contents:将文件内容读入一个字符串) 一.总结 php中读取文件内容的几种方法(file_get_contents:将文件内容读入一个字符串 ...
随机推荐
- IOS程序国际化
1.1 新建一个Single View app模版项目,命名为Localization. 1.2 新建后,可以看到工作目录结构文件如下,单击InfoPlist.strings,查看右边的属性,在Loc ...
- Mockito when(...).thenReturn(...)和doReturn(...).when(...)的区别
在Mockito中打桩(即stub)有两种方法when(...).thenReturn(...)和doReturn(...).when(...).这两个方法在大部分情况下都是可以相互替换的,但是在使用 ...
- C++中面向对象的理解
1.对于OO(面向对象)的含义,并非每一个人的看法都是同样的. 即使在如今.假设问十个人,可能会得到15种不同的答案.差点儿全部的人都会允许继承和多态是OO中的概念.大多数人还会再加上封装. 另 ...
- Idea 远程调试jenkins 项目
1.Jenkins配置 jenkins 服务启动时 需要在jvm启动项里加入如下代码: -Xdebug -Xrunjdwp:transport=dt_socket,suspend=n,server=y ...
- 【BZOJ4930】棋盘 拆边费用流
[BZOJ4930]棋盘 Description 给定一个n×n的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置(x,y),(u,v)能互相攻击当前仅 当满足以下两个条件: 1:x=u或y ...
- OpenCV玩耍(一)批量resize一个文件夹里的所有图像
鉴于用caffe做实验的时候,里面牵扯到一个问题是必须将训练集和测试集都转成256*256的图像,而官网给出的代码又不会用,所以我用opencv转了.其实opencv只转一幅图会很简单,关键在于“批量 ...
- 洛谷P2402 奶牛隐藏
洛谷P2402 奶牛隐藏 题目背景 这本是一个非常简单的问题,然而奶牛们由于下雨已经非常混乱,无法完成这一计算,于是这个任务就交给了你.(奶牛混乱的原因看题目描述) 题目描述 在一个农场里有n块田地. ...
- 我的Android进阶之旅------>Android自定义窗口标题实例
该实例的功能比较简单,但是通过该实例的扩展可以在自定义标题中做出菜单导航等实用的功能,为了实现自定义窗口标题,需要做以下几个步骤: 1.给自定义标题提供一个界面 2.将自定义标题应用给Activity ...
- LigerUI隐藏列
frozen: true 加入这个属性,此列不会在页面显示
- 教你管理SQL备份与恢复系列(1-20)
原链接:https://bbs.51cto.com/thread-1147908-1.html 教你备份与恢复数据库,直接下面下文档吧. 教你备份与恢复数据库(1)事务 http://bbs.51ct ...