【FFMPEG】从内存中获取H264数据并进行decode
版权声明:本文为博主原创文章,未经博主允许不得转载。
使用ffmpeg解码h264数据其实相对使用x264进行视频编码是简单了许多的,因为ffmpeg提供了一个decoding_encoding.c的文件,这个文件里面有简单的使用ffmpeg进行视频、音频编解码的例子,不过可能有的人也会找不到这个示例,我就讲我改造过的这个示例放在这里,同时加一些解释。
其中需要注意的的一点我需要在此说明,就是ffmpeg在进行解码的时候是会考虑要解码的数据包是否有0x00 00 001这样的头的,如果没有的话,ffmpeg会认为是错误的数据包。下面是使用opencv对解码后的图像进行显示,所以还要配置opencv的环境,如果没有的话,可以注释掉ShowImage这个函数,然后使用pgm_save这个函数将解码后的图像保存。
下面将我的代码放在下面,同样,过程参见代码注释,相对来说比较简单,不在此过多叙述:
- static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,char*filename)
- {
- FILE *f;
- int i;
- f=fopen(filename,"wb");
- fprintf(f,"P5\n%d%d\n%d\n",xsize,ysize,255);
- for(i=0;i<ysize;i++)
- fwrite(buf + i * wrap,1,xsize,f);
- fclose(f);
- }
- //通过查找0x000001或者0x00000001找到下一个数据包的头部
- static int _find_head(unsigned char*buffer, int len)
- {
- int i;
- for(i=512;i<len;i++)
- {
- if(buffer[i] == 0 && buffer[i+1] == 0 && buffer[i+2] == 0&& buffer[i+3] == 1)
- break;
- if(buffer[i]== 0 && buffer[i+1] == 0 && buffer[i+2] == 1)
- break;
- }
- if (i ==len)
- return0;
- if (i ==512)
- return0;
- return i;
- }
- //将文件中的一个数据包转换成AVPacket类型以便ffmpeg进行解码
- #define FILE_READING_BUFFER (1*1024*1024)
- static void build_avpkt(AVPacket *avpkt, FILE *fp)
- {
- static unsigned charbuffer[1*1024*1024];
- static int readptr = 0;
- static int writeptr = 0;
- intlen,toread;
- intnexthead;
- if (writeptr- readptr < 200 * 1024)
- {
- memmove(buffer, &buffer[readptr],writeptr - readptr);
- writeptr -= readptr;
- readptr = 0;
- toread = FILE_READING_BUFFER - writeptr;
- len = fread(&buffer[writeptr], 1,toread, fp);
- writeptr += len;
- }
- nexthead = _find_head(&buffer[readptr], writeptr-readptr);
- if (nexthead== 0)
- {
- printf("failedfind next head...\n");
- nexthead = writeptr - readptr;
- }
- avpkt->size = nexthead;
- avpkt->data = &buffer[readptr];
- readptr += nexthead;
- }
- static voidvideo_decode_example(const char *outfilename, constchar *filename)
- {
- AVCodec *codec;
- AVCodecContext *c= NULL;
- int frame,got_picture, len;
- FILE *f, *fout;
- AVFrame *picture;
- uint8_t inbuf[INBUF_SIZE +FF_INPUT_BUFFER_PADDING_SIZE];
- charbuf[1024];
- AVPacket avpkt;
- av_init_packet(&avpkt);
- /* set end ofbuffer to 0 (this ensures that no overreading happens for damaged mpeg streams)*/
- memset(inbuf + INBUF_SIZE, 0,FF_INPUT_BUFFER_PADDING_SIZE);
- printf("Videodecoding\n");
- opts = NULL;
- //av_dict_set(&opts,"b", "2.5M", 0);
- /* find the h264video decoder */
- codec = avcodec_find_decoder(CODEC_ID_H264);
- if (!codec){
- fprintf(stderr, "codecnot found\n");
- return ;
- }
- c = avcodec_alloc_context3(codec);
- picture= avcodec_alloc_frame();
- if(codec->capabilities&CODEC_CAP_TRUNCATED)
- c->flags|= CODEC_FLAG_TRUNCATED; /* we do not send complete frames */
- /* For somecodecs, such as msmpeg4 and mpeg4, width and height
- MUST be initialized there because thisinformation is not
- available in the bitstream. */
- /* open it */
- if(avcodec_open2(c, codec, NULL) < 0) {
- fprintf(stderr, "couldnot open codec\n");
- exit(1);
- }
- // fout=fopen(outfilename,"wb");
- /* the codec givesus the frame size, in samples */
- f = fopen(filename, "rb");
- if (!f) {
- fprintf(stderr, "couldnot open %s\n", filename);
- exit(1);
- }
- //解码与显示需要的辅助的数据结构,需要注意的是,AVFrame必须经过alloc才能使用,不然其内存的缓存空间指针是空的,程序会崩溃
- AVFrame frameRGB;
- IplImage *showImage =cvCreateImage(cvSize(352,288),8,3);
- avpicture_alloc((AVPicture*)&frameRGB,PIX_FMT_RGB24,352,288);
- cvNamedWindow("decode");
- frame = 0;
- for(;;) {
- build_avpkt(&avpkt, f);
- if(avpkt.size == 0)
- break;
- while(avpkt.size > 0) {
- len = avcodec_decode_video2(c,picture, &got_picture, &avpkt);//解码每一帧
- if(len < 0) {
- fprintf(stderr, "Error while decoding frame %d\n",frame);
- break;
- }
- if(got_picture) {
- printf("savingframe %3d\n", frame);
- fflush(stdout);
- /* thepicture is allocated by the decoder. no need to free it */
- //将YUV420格式的图像转换成RGB格式所需要的转换上下文
- SwsContext* scxt =sws_getContext(picture->width,picture->height,PIX_FMT_YUV420P,
- picture->width,picture->height,PIX_FMT_RGB24,
- 2,NULL,NULL,NULL);
- if(scxt != NULL)
- {
- sws_scale(scxt,picture->data,picture->linesize,0,c->height,frameRGB.data,frameRGB.linesize);//图像格式转换
- showImage->imageSize =frameRGB.linesize[0];//指针赋值给要显示的图像
- showImage->imageData = (char *)frameRGB.data[0];
- cvShowImage("decode",showImage);//显示
- cvWaitKey(0.5);//设置0.5s显示一帧,如果不设置由于这是个循环,会导致看不到显示出来的图像
- }
- //sprintf(buf,outfilename,frame);
- //pgm_save(picture->data[0],picture->linesize[0],
- //c->width,c->height, buf);
- //pgm_save(picture->data[1],picture->linesize[1],
- //c->width/2,c->height/2, fout);
- //pgm_save(picture->data[2],picture->linesize[2],
- //c->width/2,c->height/2, fout);
- frame++;
- }
- avpkt.size -= len;
- avpkt.data += len;
- }
- }
- /* some codecs,such as MPEG, transmit the I and P frame with a
- latency of one frame. You must do thefollowing to have a
- chance to get the last frame of the video */
- avpkt.data = NULL;
- avpkt.size = 0;
- len = avcodec_decode_video2(c, picture,&got_picture, &avpkt);
- if(got_picture) {
- printf("savinglast frame %3d\n", frame);
- fflush(stdout);
- /* the pictureis allocated by the decoder. no need to
- free it */
- sprintf(buf, outfilename, frame);
- //pgm_save(picture->data[0],picture->linesize[0],
- // c->width, c->height, fout);
- pgm_save(picture->data[0],picture->linesize[0],c->width, c->height, fout);
- pgm_save(picture->data[1],picture->linesize[1],c->width/2, c->height/2, fout);
- pgm_save(picture->data[2],picture->linesize[2],c->width/2, c->height/2, fout);
- frame++;
- }
- fclose(f);
- // fclose(fout);
- avcodec_close(c);
- av_free(c);
- av_free(picture);
- printf("\n");
- }
- int main(int argc, char* argv[])
- {
- avcodec_register_all();//注册所有的编解码器,一定要注意,如果没有这行代码则会出错,提示没有找不到编解码器
- video_decode_example("%3d.pgm","test.264");//可以使用x264编码出来的264文件
- system("pause");
- return 0;
- }
【FFMPEG】从内存中获取H264数据并进行decode的更多相关文章
- FFmpeg 如何探测网络流格式/如何从内存中获取数据
文章转自:http://blog.csdn.net/rootusers/article/details/42551935 一般ffmpeg都是直接从文件中读取或者从网络流中读取,比如rtp://xx. ...
- C# 操作地址 从内存中读取写入数据(初级)
本示例以植物大战僵尸为例, 实现功能为 每1秒让阳光刷新为 9999.本示例使用的游戏版本为 [植物大战僵尸2010年度版], 使用的辅助查看内存地址的工具是 CE. 由于每次启动游戏, 游戏中阳光 ...
- <转>libjpeg解码内存中的jpeg数据
转自http://my.unix-center.net/~Simon_fu/?p=565 熟悉libjpeg的朋友都知道libjpeg是一个开源的库.Linux和Android都是用libjpeg来 ...
- 通过DialogFragment从DatePicker或TimePicker中获取日期数据
通过DialogFragment从DatePicker或TimePicker中获取日期数据 一个activity类,里面存有date和time的变量,想通过dialogfragment的方式获取用户输 ...
- ffmpeg 从内存中读取数据(或将数据输出到内存)
更新记录(2014.7.24): 1.为了使本文更通俗易懂,更新了部分内容,将例子改为从内存中打开. 2.增加了将数据输出到内存的方法. 从内存中读取数据 ffmpeg一般情况下支持打开一个本地文件, ...
- ffmpeg 从内存中读取数据(或将数据输出到内存)(转)
更新记录(2014.7.24): 1.为了使本文更通俗易懂,更新了部分内容,将例子改为从内存中打开. 2.增加了将数据输出到内存的方法. 从内存中读取数据 ffmpeg一般情况下支持打开一个本地文件, ...
- ffmpeg 从内存中读取数据 .
http://blog.csdn.net/leixiaohua1020/article/details/12980423 ——————————————————————————————————————— ...
- 【在网页中获取截图数据】Chrome和Firefox下的实战经验
[转载自我在segmentfault的专栏:https://segmentfault.com/a/1190000004584071] 最近在实现一个功能,需求如下: 前提:当前页面无弹窗 页面任意位置 ...
- Android中获取网络数据时的分页加载
//此实在Fragment中实现的,黄色部分为自动加载,红色部分是需要注意的和手动加载, 蓝色部分是睡眠时间,自我感觉不用写 ,还有就是手动加载时,不知道为什么进去后显示的就是最后一行,求大神 ...
随机推荐
- hivesql中的concat函数,concat_ws函数,concat_group函数之间的区别
一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串.使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为 +---- ...
- 牛客国庆集训派对Day6 && CCPC-WannaFly-Camp #1 F. kingdom(DP)
题目链接:https://www.nowcoder.com/acm/contest/206/F 题意:一棵 n 个点的树,根为 1,重儿子到父亲的费用为 0,其余为 1,问所有点到 1 的最大总费用是 ...
- django.core.exceptions.ImproperlyConfigured: The included URLconf 's9luffycity.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused
出现问题: $ python manage.py runserver 启动项目报错时候 raise ImproperlyConfigured(msg.format(name=self.urlconf_ ...
- windows——celery
celery 4.0版本以后放弃对Windows的支持 GITHUB_issues(https://github.com/celery/celery/issues/4178) 替代解决方案: 安装:p ...
- 【Android-SwipeRefreshLayout控件】下拉刷新
Android自带API ,V4包下面的下拉刷新控件 android.support.v4.widget.SwipeRefreshLayout SwipeRefreshLayout只能包含一个控件 布 ...
- MFC GDI绘图
DC——MFC设备描述表类(也叫设备环境.设备上下文).默认起始点(0,0),带To的函数会移动起始点到指定位置. 新建单个文档的MFC应用程序,类视图——View项的属性——消息,WM_PAINT, ...
- Luogu P1903 [国家集训队]数颜色 or 维护队列
标准的带修莫队...咕到了现在$qwq$ 莫队是对询问排序来优化复杂度的(不带修就是对询问区间$[l,r]$排序).. 那么现在带修了,我们再可以维护一个时间维度$tm$:对于每个询问,每次回答前先检 ...
- Django-安装/分组命名/路由分发
一.安装Django 命令行窗口: pycharm安装: 二.创建Django项目 命令行窗口创建项目: 访问地址: 表示访问成功 注意如果我们在命令行窗口创建的应用需要我们手动的在django的se ...
- vmware 中Linux系统怎么连接外网?
VMware虚拟机有三种网络模式,分别是Bridged(桥接模式).NAT(网络地址转换模式).Host-only(主机模式). VMware workstation安装好之后会多出两个网络连接,分别 ...
- logserver 日志服务项目发布
logserver是使用logback.light-4j.commons-exec等构建的简单日志服务,参考项目logbackserver和light4j,支持跟踪日志.分页查看.搜索定位.下载文件等 ...