由于工作需要,所以基本了解了一下视频的解码流程。

参考教程为:

1、王纲的《跟我一起学FFmpeg》系列

2、雷霄骅雷神的博客

原理部分暂时没有整理,后期可以补充一下知识。

ffmepg的api使用方面

1、打开一个输入流

2、设置解码器

3、读取每一个包,并获取到一帧的数据

4、交给解码器解码

下面的代码就是,读取一个视频或者文件,将其中的一帧图片保存为BGR24格式的文件,该文件加上BMP文件头即可使用图片浏览器打开。需要注意的是,这样会将图片上下翻转。所以图片会倒着显示。

#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/mem.h>
#include <libswscale/swscale.h>
#include <libavutil/file.h>
#include <libavutil/imgutils.h>#include <stdio.h>#define myprintf(x) printf(x)unsigned char *filecontent;
unsigned char *content; static int parse_cmd(int argc, char * argv[])
{int ret = 0;if (argc < 2)return -1;return ret;
}static void show_help()
{printf("Input error!\n");printf("Usage: ./decodec-and-display-ffmpeg <filepath/file>\n");
}void add_header()
{filecontent     = (unsigned char*) malloc (0x5eec36);content         = filecontent + 0x36;FILE *file = fopen("./1920*1080.bmp", "rb");if (file < 0){myprintf("file can not open!\n");return;}printf("1920*1080.bmp open success!\n");fread(filecontent, 1, 0x36, file);printf("finish reading 1920*1080.bmp header!\n");fclose(file);file = fopen("./fpsave.bgr24", "rb");fread(content, 1, 0x5eec00, file);myprintf("finish reading ./fpsave.bgr24!\n");fclose(file);file = fopen("./fpsave-bgr24.bmp", "wb+");fwrite(filecontent, 1, 0x5eec36, file);fclose(file);myprintf("Add header finish!\n");
}int main(int argc, char *argv[])
{int  ret, got_picture;int  i, videoindex = -1;                  //这两个变量,主要是从流中找到视频流的编号,而非音频流char filepath[128];                       //输入文件路径int  dst_bytes_num;                         //一幅解码后的BGR24格式图片存储所需的内存AVFormatContext     *pFormatCtx;            //上下文结构体,包含了所有的信息AVCodecContext      *pCodecCtx;             //解码器用到的上下文结构体AVCodec           *pCodec;                //解码器实例AVFrame              *pFrame,*pFrameBGR;     //存放帧和BGR数据的结构体AVPacket          *avpacket = NULL;         //这个结构体很关键,其中包含了ffmpeg从输入流中得到的裸流的数据和大小FILE              *fpSave;                //先将解码后的数据保存到这个文件中,以便检验uint8_t        *out_buffer;            //解码后的帧数据缓存struct SwsContext *img_convert_ctx;          //格式转换需要用到的结构体,暂时不对视频进行格式转换/* 0、简单判断输入否合法 */ret = parse_cmd(argc, argv);if (ret < 0){show_help();return 0;}else {printf("%s\n", argv[1]);}/* 1、初始化ffmpeg */av_register_all();/* 2、打开输入流 *//* 2.1打开网络流或文件流 */pFormatCtx = avformat_alloc_context();                                  //分配一个输入流的上下文结构体,用以存储相关的数据              ret = avformat_open_input(&pFormatCtx, argv[1], NULL, NULL);            //以设定的方式(参数)打开输入流,并与pFormatCtx结构体关联,但是此时未填充if(ret)  {printf("Couldn't open input stream.\n");return -1;}/* 2.2获取流信息,并保存在上下文pFormatCtx中 */ret = avformat_find_stream_info(pFormatCtx, NULL);                      //通过该函数,填充pFormatCtx结构体,如编码方式,流(音频、视频)的个数,宽高,帧率等if(ret){printf("Couldn't find stream information.\n");return -1;}/* 2.3打印出输入文件信息 */printf("---------------- File Information ---------------\n");av_dump_format(pFormatCtx, 0, argv[1], 0);printf("-------------------------------------------------\n");/* 2.4从流中,找出视频流编号 */videoindex=-1;                                 for( i = 0; i < pFormatCtx->nb_streams; i++) {if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) //我们不关心音频AVMEDIA_TYPE_AUDIO,所以找出来视频流AVMEDIA_TYPE_VIDEO的编号,通常为0{videoindex=i;//printf("videoindex = %d\n", videoindex);break;}}if(-1 == videoindex){printf("Didn't find a video stream.\n");return -1;}/* 3、解码输入流,获取到一帧的数据 *//* 3.1根据编码类型查找解码器 */pCodecCtx = pFormatCtx->streams[videoindex]->codec;                        //获取到视频的编码类型,对于MP4,pFormatCtx->streams[videoindex]->codec->codec_id = 13,即AV_CODEC_ID_MPEG4//printf("pCodecCtx = pFormatCtx->streams[videoindex]->codec->codec_id = %d \n", pFormatCtx->streams[videoindex]->codec->codec_id);pCodec = avcodec_find_decoder(pCodecCtx->codec_id);                  //用于查找FFmpeg的AV_CODEC_ID_MPEG4格式对应的解码器if(pCodec==NULL){printf("Codec not found.\n");return -1;}/* 3.2打开解码器 */if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){printf("Could not open codec.\n");return -1;}/* 3.2为解码工作,分配存储空间 *///为一帧编码的数据分配内存空间,这个函数只是分配AVFrame结构体,但data指向的内存并没有分配,需要我们指定,这个内存的大小就是一张特定格式图像所需的大小。pFrame=av_frame_alloc();  //为一帧编解码后的BGR24数据分配一个结构体,这个函数只是分配AVFrame结构体,但data指向的内存并没有分配,需要我们指定,这个内存的大小就是一张特定格式图像所需的大小。pFrameBGR=av_frame_alloc();  //分配的out_buffer大小是宽*高*3(RGB24)对于测试视频320*240*3=230400Byte,该buffer只是一个缓存dst_bytes_num = avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height);out_buffer=(uint8_t *)av_malloc(dst_bytes_num);if (out_buffer){printf("out_buffer alloc success!\n");printf("address of out_buffer = %p\n", out_buffer);}else{printf("out_buffer alloc failed!\n");return 0;}//前面的av_frame_alloc函数,只是为这个AVFrame结构体分配了内存,而该类型的指针指向的内存还没分配。这里把av_malloc得到的内存和AVFrame关联起来,当然,其还会设置AVFrame的其他成员//该函数会初始化pFrameBGR->linesizeret = av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, out_buffer, AV_PIX_FMT_BGR24,pCodecCtx->width, pCodecCtx->height,1);//经过测试,pFrameBGR->linesize[4]的初始值均为0,经过上面的初始化后,分别被赋值;pFrameBGR->data[4]为4个指针,初值均为NULL,后续被分配到内存//如果参数是YUV420P,pFrameBGR->linesize[0] = width, pFrameBGR->linesize[1] = width / 2, pFrameBGR->linesize[2] = width / 2//如果参数是BGR24,pFrameBGR->linesize[0] = width * 3, pFrameBGR->linesize[1] = 0, pFrameBGR->linesize[2] = 0//printf("pFrameBGR->linesize[0] = %d, pFrameBGR->linesize[1] = %d, pFrameBGR->linesize[2] = %d, pFrameBGR->linesize[3] = %d\naddress of pFrameBGR->data[0] = %p, address of pFrameBGR->data[1] = %p, address of pFrameBGR->data[2] = %p\n", pFrameBGR->linesize[0], pFrameBGR->linesize[1], pFrameBGR->linesize[2], pFrameBGR->linesize[3], pFrameBGR->data[0], pFrameBGR->data[1], pFrameBGR->data[2]);//为AVPacket结构体分配缓存avpacket=(AVPacket *)av_malloc(sizeof(AVPacket));//对转换进行设置,这里要设置转换源的大小、格式和转换目标的大小、格式,以及转换所用的算法//img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);if (img_convert_ctx == NULL)  {  printf("sws_getContext failed!\n ");  return -1;  }if ((fpSave = fopen("./fpsave.bgr24", "wb+")) == NULL) //data数据保存的文件名return 0; int cnt = 0;int cnt_decode = 0;while(av_read_frame(pFormatCtx, avpacket)>=0){if(avpacket->stream_index==videoindex){ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, avpacket);  //真正的解码工作,将压缩的数据解码为yuv420p格式if(ret < 0)  {  printf("解码错误\n");  return -1;  }else{cnt_decode++;}if(got_picture){printf("解码一帧所需的次数cnt_decode = %d\n", cnt_decode);printf("解码成功\n"); sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameBGR->data, pFrameBGR->linesize);printf("YUV420P->BGR24格式转换完成\n"); //y_size=pCodecCtx->width*pCodecCtx->height*3;  if (cnt == 0)fwrite(pFrameBGR->data[0], 1, dst_bytes_num, fpSave);    //BGR24//fwrite(pFrameBGR->data[0], 1, y_size,  fpSave);   //Y //fwrite(pFrameBGR->data[1], 1, y_size/4,fpSave);   //U//fwrite(pFrameBGR->data[2], 1, y_size/4,fpSave);   //Vprintf("Succeed to decode 1 frame!\n");printf("avpacket->stream_index = %d\n", avpacket->stream_index);printf("avpacket->size = %d, cnt = %d\n", avpacket->size, cnt);//fwrite(avpacket->data,1,avpacket->size,fpSave);//写数据到文件中cnt++;if (cnt == 1)break;}}av_free_packet(avpacket);}fclose(fpSave);add_header();av_frame_free(&pFrameBGR);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);return 0;
}

ffmpeg学习——基本的解码流程相关推荐

  1. FFmpeg学习 avcodec软解码函数分析

    前言 本文分析ffmpeg软解码流程,相关函数如下,以find_stream_info中的try_decode_frame为例: 相关函数都在libavcodec包下. 基本调用流程如下: const ...

  2. FFmpeg学习笔记之ffplay流程分析

    背景说明 FFmpeg是一个开源,免费,跨平台的视频和音频流方案,它提供了一套完整的录制.转换以及流化音视频的解决方案.而ffplay是有ffmpeg官方提供的一个基于ffmpeg的简单播放器.学习f ...

  3. 【FFmpeg】详解FFmpeg解封装、解码流程

    目录 1.获取媒体信息头 2.获取媒体流信息 3.准备解码器 3.1 获取视频.音频.字幕流在解封装上下文 AVFormatContext 的流列表 AVStream **streams 中的索引 3 ...

  4. 【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)

    一.前言 在正式编写 FFmpeg 播放器前,我们需要先简单了解下所要用到的 FFmpeg 库.播放与解码流程.函数和相关结构体. 二.FFmpeg 库简介 库 介绍 avcodec 音视频编解码核心 ...

  5. FFmpeg解码流程简介

    本文基于雷神的<基于 FFmpeg + SDL 的视频播放器的制作>课程的视频 ,本文就是基于该系列文章的学习后,总结出来的学习经验.如果想细致了解更多方法的使用,可以参考雷神的FFmpe ...

  6. 基于Intel 集成显卡的 FFmpeg 调用 VAAPI 硬件解码零数据拷贝链接推理引擎工作流程的实现

    概述 在视频处理流程中,视频的解码通常在 CPU 中进行,若用户需要使用集成显卡进行深度学习推理,解码数据需要从 CPU的缓存中拷贝至集成显卡中进行推理.本文旨在通过集成显卡进行硬件解码,使用FFmp ...

  7. ffmpeg解码流程 turorial5详解

    From: http://www.360doc.com/content/11/1117/09/8050095_165108638.shtml FFMPEG解码流程 1. 注册所有容器格式和CODEC: ...

  8. 264编码基本概念 FFMpeg的解码流程

    下面转自http://topic.csdn.net/u/20081020/16/7156e0b2-dbfb-4b4f-af59-2be04cf9a420.html 的8楼 1.NAL.Slice与fr ...

  9. Intel MediaSDK sample_decode 官方GPU解码流程学习(二) - 在双显卡机器上实现DirectX11 D3D11和OpenCL共享资源

    很久以前写过有关D3D11和OCL直接共享显存的代码, Intel MediaSDK sample_decode 官方GPU解码流程学习 - DirectX11 D3D11和OpenCL共享资源 这段 ...

最新文章

  1. AlphaCode惊世登场!编程版“阿法狗”悄悄参赛,击败一半程序员
  2. swoole原生mysql进程池_swoole的mysql连接池怎么弄
  3. 关于产品经理如何准备面试,我有三点想法
  4. 概率论和数理统计 - 01
  5. 【IDEA】IDEA 单元测试 System.in 没办法输入
  6. centos 低版本出现fatal: unable to access 'https://github.com/XXXX': SSL connect error
  7. tesseract如何在Linux下卸载,Tesseract装配
  8. Carryon 数数字(x^n ≡1 mod(x-1))
  9. ETOKEN 身份认证 电子证书
  10. 计算机学院运动会解说词,学院运动会入场解说词
  11. Android 版本更新,支持增量更新
  12. python turtle画简易的太极图
  13. 云计算作者姚宏宇1月26日中关村图书大厦讲座
  14. Unity常见插件汇总
  15. 纺织再生产品申请grs注意标准
  16. PowerDesigner 生成数据字典
  17. linux运维管理系统培训,Linux运维教程之Linux系统用户与组管理
  18. EndNote20.5 for Mac安装教程
  19. Java学习网站有哪些
  20. 【命令】如何将文件夹(永久)映射成虚拟硬盘

热门文章

  1. 跑步,读书,成为更好的自己,结识志同道合的小伙伴
  2. freeRTOS中文实用教程1
  3. threejs实现汽车展览
  4. 如何去掉idea的诸多警告
  5. 5w每秒的高并发优化:电商秒杀与抢购
  6. c语言字符串函数难题,一些常见的C语言字符串操作
  7. 解决RDP自动锁屏睡眠问题
  8. PPT幻灯片高级使用技巧
  9. Cesium 1.91 更新日志 - MSAA 与原生 Promise 来了
  10. java中byte数组与int类型的转换(两种方式)