解码文件中的视频流,用SDL2显示,没有做时间同步。

步骤很简单:打开文件,demuxer,decoder,把frame格式转成需要的格式,显示。

直接代码:


extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libswresample/swresample.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
}#include <string>
#include <iostream>
using namespace std;int main(int argc, char *argv[])
{AVFormatContext *pformatContext = nullptr;AVCodecContext  *pcodecContext  = nullptr;AVCodec         *pcodec         = nullptr;const char      *pinputfile     = argv[1];unsigned int     videoStream    = -1;int              iret           = 0;if(argc < 2){pinputfile = "huachi_part.mp4";}if(SDL_Init(SDL_INIT_VIDEO)){cerr << "SDL_Init video fail:" << SDL_GetError() << endl;exit(1);}/*** 初始化libavformat库,并注册所有的合成器(muxer)、分离器(demuxer)和协议(protocol)*/av_register_all();/***打开输入流,并读取流的头部(header)。*`pformatContext`可以是通过`avformat_alloc_context()`分配得到的`AVFormatContext`,也可以是`NULL`,如果是`NULL`,则该函数会给其分配空间并赋值。*   第三个参数是指定输入格式,直接用`NULL`,则自动检测。*   第四个参数是`AVDictionary **`,一般直接用`NULL`,除非给`AVFormatContext`传递一些私有参数。*此函数只是打开输入流,相应的解码器并没有打开。*打开的流必须使用`avforamt_close_input()`关闭。*/iret = avformat_open_input(&pformatContext, pinputfile, nullptr, nullptr);if(iret < 0){cerr << "avforamt_open_input fail:" << iret << endl;exit(1);}/*** 获取流的信息,一些文件格式(如MPEG)没有头部(header),可以使用这个函数来获取流的信息*/avformat_find_stream_info(pformatContext, nullptr);/*** 打印输入或输出格式的详细信息,第4个参数指定是输入(0)还是输出(1)。*/av_dump_format(pformatContext, 0, pinputfile, 0);/*** 查找视频流*/for(unsigned int i = 0; i < pformatContext->nb_streams; ++i){/*** 使用 AVStream 中的 codecpar 字段,codec 字段被标记为 deprecated*/if(pformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){videoStream = i;break;}}if(-1 == videoStream){cerr << "could not find video stream in " << pinputfile << endl;exit(1);}/*** 使用引用计数*/AVDictionary *opts = nullptr;av_dict_set(&opts, "refconted_frames" , "1", 0);/*** 创建一个AVCodecContext,并设置其中的字段为默认值。** 新创建一个AVCodecContext,AVStream 中的 codec 被标记为 deprecated* * 必须使用 avcodec_free_context() 释放。*/pcodecContext = avcodec_alloc_context3(nullptr);/*** 根据 codecpar 中的值填充 codeccontext 中的相应字段。* 返回负值时表示失败。*/avcodec_parameters_to_context(pcodecContext, pformatContext->streams[videoStream]->codecpar);/*** 使用 codeid 找到已注册的解码器*/pcodec = avcodec_find_decoder(pcodecContext->codec_id);/*** 初始化 codeccontext,以使用指定的 codec。*/avcodec_open2(pcodecContext, pcodec, &opts);AVPacket    pkt;AVFrame    *pframe;AVFrame    *pframeYUV;SwsContext *pswscontext = nullptr;int         numBytes    = 0;uint8_t    *buffer      = nullptr;/*** 初始化 AVPacket 中的可选字段为默认值。* 结构中的 data 和 size 未设置,需要单独初始化。** AVPacket 结构中的一些成员:*    AVBufferRef *buf;  使用引用计数时保存packet数据的引用;若为NULL,则表示packet数据不是引用计数。*    int64_t pts;*    int64_t dts;*    uint8_t *data;*    int   size;*    int   stream_index; 用来判断这个packet是音频还是视频或字幕等。* */av_init_packet(&pkt);pkt.data = nullptr;pkt.size = 0;pframe = av_frame_alloc();pframeYUV = av_frame_alloc();/*** 返回按指定格式保存图片时需要的大小。第4个参数指定 linesize alignment 。*/numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pcodecContext->width, pcodecContext->height, 4);buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));/*** 按指定的格式,用提供的数组来填充 data 和 linesize 。* 当 buffer 为 NULL 时,返回 buffer 需要的大小。** 返回 buffer 需要的大小,负值时表示失败。*/av_image_fill_arrays(pframeYUV->data, pframeYUV->linesize, buffer, AV_PIX_FMT_YUV420P,pcodecContext->width, pcodecContext->height, 4);/*** 用来进行缩放或转换操作。参见sws_scale()。* * 使用SDL来显示视频中的每一帧图像。显示时使用YUV420P格式,但视频流中解码后的格式可能不是这种格式,需要转换。** 根据参数,若当前的swscontext还可以用,则继续使用;否则释放当前swscontext并使用新参数重新创建一个swscontext。* 若pswscontext为NULL,则等同于 sws_getContext()*/pswscontext = sws_getCachedContext(pswscontext, pcodecContext->width, pcodecContext->height, pcodecContext->pix_fmt,pcodecContext->width, pcodecContext->height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, nullptr, nullptr, nullptr);SDL_Window    *pwindow   = nullptr;SDL_Renderer  *prenderer = nullptr;SDL_Texture   *ptexture  = nullptr;SDL_Rect       rect;SDL_CreateWindowAndRenderer(pcodecContext->width, pcodecContext->height, SDL_WINDOW_OPENGL,&pwindow, &prenderer);ptexture = SDL_CreateTexture(prenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pcodecContext->width, pcodecContext->height);rect.x = 0;rect.y = 0;rect.w = pcodecContext->width;rect.h = pcodecContext->height;/*** 读取输入流。* 该函数只返回存储在文件中的信息,并不保证得到的信息对解码器可用。** 成功时返回0,若有错误或到达文件结尾,返回负值。*/while(av_read_frame(pformatContext, &pkt) >= 0){/*** 当 pkt.buf 为 NULL 时,这个 pkt 只在下一次调用av_read_frame()前或调用* avformat_close_input()前可用。否则,这个 pkt 一直可用,因为非NULL时,表示是* 引用计数的,pkt本身获取了一个引用。 * 不论 pkt.buf 是否为 NULL ,当 pkt 不再需要时,都需要使用 av_packet_unref()来释放。** 对视频,一个 pkt 正好包含一个 frame; 对于音频,如果每个 frame 是固定大小(如PCM、ADPCM),则一个 pkt 包含* 整数个 frame,如果音频frame是可变大小的(如 MPEG音频),则一个 pkt 包含一个 frame。** pkt.pts, pkt.dts, pkt.duration会设置为基于 AVStream.time_base为单位的值。* pkt.pts 可能为 AV_NOPTS_VALUE.*//* 当前只处理视频流的packet */if(pkt.stream_index == videoStream){/*** NEW API, legacy API avcodec_decode_video2()** 给解码器提供裸包数据作为输入** pkt的所有者不会改变,仍时调用者。* 与旧的API不同,此函数总是会把整个 pkt 消费掉。如果 pkt 含有多个 frame,则在下一次调用* 此函数前,需要多次调用 avcodec_receive_frame()。* pkt 可以为NULL,为NULL时,表示这是一个flush packet,代表到达流的结尾。* 如果解码器中还有frame,则在发送flush packet后,还是会返回这些frame。** 成功时返回0,出错时返回负值。*  AVERROR(EAGAIN) : 需要receive读出已有的输出*  AVERROR_EOF : 解码器已被flush, 不能再发送新的pkt*/iret = avcodec_send_packet(pcodecContext, &pkt);for(;;){/*** 返回解码器中已解码的数据。** pframe:由解码器分配的引用计数的音频或视频frame。这个函数做的第一件事就是*  av_frame_unref(pframe)。** 成功时返回0, 出错时返回负值。*   AVERROR(EAGAIN) : 需要再次send新的输入*   AVERROR_EOF : 解码器已被flush,不会再输出frame*/iret = avcodec_receive_frame(pcodecContext, pframe);if(iret == 0){sws_scale(pswscontext, (uint8_t const* const*)pframe->data, pframe->linesize, 0,pcodecContext->height, pframeYUV->data, pframeYUV->linesize);SDL_UpdateTexture(ptexture, &rect, pframeYUV->data[0], pframeYUV->linesize[0]);SDL_RenderClear(prenderer);SDL_RenderCopy(prenderer, ptexture, &rect, &rect);SDL_RenderPresent(prenderer);}else{break;}}}av_packet_unref(&pkt);}SDL_Quit();av_free(buffer);av_frame_free(&pframe);av_frame_free(&pframeYUV);avcodec_free_context(&pcodecContext);avformat_close_input(&pformatContext);return 0;
}

转载于:https://my.oschina.net/u/2286208/blog/751451

老调重弹-ffmpeg解码视频图像相关推荐

  1. ffmpeg解码视频存为BMP文件

    ffmpeg解码视频存为BMP文件 分类: ffmpeg2011-07-28 12:13 8人阅读 评论(0) 收藏 举报 view plain #include <windows.h> ...

  2. 从零实现简易播放器:4.ffmpeg 解码视频为yuv数据-使用avcodec_send_packet与avcodec_receive_frame

    ffmpeg 解码视频为yuv数据 作者:史正 邮箱:shizheng163@126.com 如有错误还请及时指正 如果有错误的描述给您带来不便还请见谅 如需交流请发送邮件,欢迎联系 csdn : h ...

  3. ffmpeg解码视频文件并播放

    最近学习了一下如何使用ffmpeg解码音视频,网上的教程挺多但是也挺杂的,搞了好几天,明白了ffmpeg解码音视频的大体流程,这里记录一下ffmpeg解码视频并播放音视频的例子,但并没有做音频.视频播 ...

  4. ffmpeg 解码视频(h264、mpeg2)输出yuv420p文件

    ffmpeg 解码视频(h264.mpeg2)输出yuv420p文件 播放yuv可以参考:ffplay -pixel_format yuv420p -video_size 768x320 -frame ...

  5. ffmpeg 解码视频小例子

    原:http://blog.csdn.net/flyfight88/article/details/8541068 ffmpeg是编解码的利器,用了很久,以前看过dranger 的教程,非常精彩,受益 ...

  6. FFmpeg解码视频并保存为图片

    1.多媒体文件的读取 一个多媒体文件包含有多个流(视频流 video stream,音频流 audio stream,字幕等):流是一种抽象的概念,表示一连串的数据元素:     流中的数据元素称为帧 ...

  7. FFmpeg -- 解码视频

    ffmpeg是编解码的利器,用了很久,以前看过dranger 的教程,非常精彩,受益颇多,是学习ffmpeg api很好的材料.可惜的是其针对的ffmpeg版本已经比较老了,而ffmpeg的更新又很快 ...

  8. ffmpeg解码视频

    目录 一.前言 二.ffmpeg解码API介绍 三.ffmpeg解码示例 四.ffmpeg解码框架设计 <ffmpeg解码H264/H265为yuv代码实现>链接: https://edu ...

  9. ffmpeg解码后图像呈绿色

    / 偶然发现 网站: 智城 让外包更简单~   有类似项目需求,可参考 http://www.taskcity.com/pages/search_projects_advance?keywords=& ...

最新文章

  1. c#创建、保存excel正常执行要点补疑
  2. kerberos java实现,基于kerberos实现jaas登录
  3. Android - TextureView, SurfaceView和GLSurfaceView 以及 SurfaceTexture
  4. MapReduce-计数器
  5. kubernetes(一)kubeadm搭建k8s集群
  6. wpf 怎样判断是否选中 checkbox_怎样判断自己的肌肤是否缺水?
  7. 自动生成小学四则运算题目
  8. centos7启用EPEL Repository
  9. android时间显示秒,MIUI 12桌面如何显示带秒时钟?
  10. 浏览器-解决火狐浏览器总是提示Adobe Flash更新的问题
  11. 了解车辆驾驶行为、成功验证C-V2X技术
  12. 科普博客:摩尔定律和安迪-比尔定律
  13. HTML背景样式简单介绍
  14. php解析视频_【教程】php实现百度网盘视频解析
  15. 因式分解结合最近邻:多层面的协同过滤模型
  16. 针对高分辨率雷达和相机的无标定板的像素级外参自标定方法
  17. 每日经典算法题(十三) 逆推算法(平方根相关)
  18. 图书借阅管理系统——C++版
  19. 如何优化selenium webdriver的执行速度
  20. 简述u盘安装计算机系统的方法,计算机装系统需要U盘?最简单的方法

热门文章

  1. 攻防世界高手进阶区 ——forgot
  2. 2020,那些惊艳我的产品迭代
  3. allegro放置器件无法放_allegro 放置元件处无复选框,导致无法放置元件错误。
  4. 【电子取证:镜像仿真篇】DD、E01系统镜像动态仿真
  5. linux系统下html中文乱码
  6. 小情侣冷战到半夜!不料程序员男友一波“神操作”巧妙化解,女友懵了.........
  7. android全景设置高度,Android全景SDK | 百度地图API SDK
  8. objection 基础知识
  9. 交换机和路由器的区别?
  10. 2022 Java面试题道通科技