文档章节

ffmpeg解码视频并输出到屏幕

曾经花田错
 曾经花田错
发布于 2016/12/07 14:49
字数 1019
阅读 141
收藏 0

* 学习自雷神的博客和视频,图片来自他的课件,雷神博客:* 

http://blog.csdn.NET/leixiaohua1020/

 

ffmpeg基础知识

ffmpeg的库

  • avdecoc: 编解码
  • avformat: 封装格式的处理(mkv,mp4,avi)
  • swscale: 视频像素数据格式转换(常用于解码后视频的裁剪)
  • avutil: 工具库
  • avfilter: 滤镜特效处理
  • avdevice: 各种设备的输入输出
  • postproc: 后加工
  • swresample: 音频采样数据格式转换

ffmpeg的执行流程

        

  • avcodec_decode_open2()这个函数是解码函数, 最主要的一个函数。
  •   图中解码流程是:获取一个pakcet, 然后调用解码函数,把AVPacket结构中的data转换为AVFrame结构的data。
  • AVPacket结构存储一帧压缩的编码数据。
  • AVFrame结构存储一帧解码后的像素数据(对音频则是采样数据)。
  • AVFrame结构的元素data是双重指针,YUV数据来说包含data[0],data[1],data[2]分别存Y、U、V数据,注意每帧中U、V数据是Y数据的四分之一大小(对420P来说)。『Y:亮度数据, U,V:色差数据,由于人的眼睛对亮度更敏感,故而YUV数据中存更多的Y而减少UV的数据。当只有Y数据的时候,显示为黑白』 
  • 解码出来的数据可能函数无效像素。需要用sws_scale()函数处理。
  • 如图: 

         

 

ffmpeg 解码相关结构体

        

  • AVFormatContext是一个统筹全局的结构, 包含一些视频文件名,视频时长,视频码率等封装格式信息。 
  • AVInputFormat包含一些具体的视频格式信息,每种视频格式对应一个这个结构。
  •  一般来说视频文件有两个流:视频流和音频流。有几个流就有几个AVStream数据结构, 一般视频流的index==0(也有其他情况), AVStream在AVFormatContext中是一个双重指针。 
  • AVCodecContext 编解码器上下文结构体,保存音视频编解码相关信息。
  • AVCodec 每种视/音频编解码器(例如h264)对应一个该结构体。

SDL显示YUV图像流程:

        

  •  SDL_Surface就是使用SDL的时候弹出的那个窗口。
  • SDL_Overlay用于显示YUV数据。一个SDL_Overlay对应一帧YUV数据。
  • SDL_Rect用于确定SDL_Overlay显示的位置。

代码 

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

int main(int argc, char *argv[]) {
    AVFormatContext *pFormatCtx = NULL;
    int             i, videoStream;
    AVCodecContext  *pCodecCtx = NULL;
    AVCodec         *pCodec = NULL;
    AVFrame         *pFrame = NULL;
    AVPacket        packet;
    int             frameFinished;

    AVDictionary    *optionDict = NULL;
    struct SwsContext *sws_ctx = NULL;

    SDL_Overlay     *bmp = NULL;
    SDL_Surface     *screen = NULL;
    SDL_Rect        rect;
    SDL_Event       event;

    if(argc < 2){
        fprintf(stderr, "Usage: test <file> \n");
        exit(1);
    }

    av_register_all();

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
        fprintf(stderr,"Could not initialize SDL - %s " + *SDL_GetError());
        exit(1);
    }

    /*
    *打开一个文件
    */
    if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
        return -1;

    /*
	 *为pFormatCtx->streams填充上正确的信息
	 */
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
        return -1;

    /*
	 *手工调试函数,将文件信息在终端输出
	 */
    av_dump_format(pFormatCtx, 0, argv[1], 0);


    videoStream=-1;
	for ( i = 0; i < pFormatCtx->nb_streams; i++)
	  if(pFormatCtx -> streams[i] -> codec -> codec_type == AVMEDIA_TYPE_VIDEO) {
	    videoStream = i;
	    break;
	  }

	if(videoStream == -1)
	  return -1;

    /*
     *从 vedio stream 中获取对应的解码器上下文的指针
     */
    pCodecCtx = pFormatCtx -> streams[videoStream] -> codec;

    /*
     *根据 codec_id 找到对应的解码器
     */
    pCodec = avcodec_find_decoder(pCodecCtx -> codec_id);

    if(pCodec == NULL){
        fprintf(stderr, "Unsupported codec ! \n");
        return -1;
    }

    /*
     * 打开解码器
     */
    if(avcodec_open2(pCodecCtx, pCodec, &optionDict) <0 )
        return -1;

    /*
     * 为frame 申请内存
     */
    pFrame = av_frame_alloc();

    #ifdef __DARWIN__
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
    #else
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
    #endif // __DARWIN__

    if(!screen){
        fprintf(stderr, "SDL : could not set video mode - exiting \n");
        exit(1);
    }

    /*
     * 申请一个 overlay , 将 yuv数据给 screen
     */
    bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);

    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                             AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);

    i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0){

        if(packet.stream_index == videoStream){
            
            //为视频流解码
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

            if(frameFinished){
                SDL_LockYUVOverlay(bmp);
             
                AVPicture pict;
                pict.data[0] = bmp->pixels[0];
                pict.data[1] = bmp->pixels[2];
                pict.data[2] = bmp->pixels[1];

                pict.linesize[0] = bmp->pitches[0];
                pict.linesize[1] = bmp->pitches[2];
                pict.linesize[2] = bmp->pitches[1];

                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize,
                          0, pCodecCtx->height, pict.data, pict.linesize);

                SDL_UnlockYUVOverlay(bmp);

                rect.x = 0;
                rect.y = 0;
                rect.w = pCodecCtx->width;
                rect.h = pCodecCtx->height;

                SDL_DisplayYUVOverlay(bmp, &rect);
                SDL_Delay(10);

            }
        }

        av_free_packet(&packet);
        SDL_PollEvent(&event);

        switch (event.type) {

            case SDL_QUIT:
                SDL_Quit();
                exit(0);
                break;

            default:
                break;
        }

    }


    av_free(pFrame);

    avcodec_close(pCodecCtx);

    avformat_close_input(&pFormatCtx);

    return 0;
}

编译:

gcc -o playvedio playvedio.c -lavutil -lavformat -lavcodec -lavutil -lswscale -lSDL

运行:

./playvedio 文件路径+文件名

 

© 著作权归作者所有

共有 人打赏支持
曾经花田错
粉丝 5
博文 42
码字总数 39130
作品 0
海淀
程序员
学习FFmpeg API – 解码视频

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

霄霄月月
2012/05/08
0
32
FFmpeg深入分析之零-基础

FFmpeg是相当强大的多媒体编解码框架,在深入分析其源代码之前必须要有基本的多媒 体基础知识,否则其源代码会非常晦涩难懂。本文将从介绍一些基本的多媒体只是,主要是为研读ffmpeg源代码做...

李荣刚
2015/03/16
0
0
FFmpeg深入分析之零-基础

FFmpeg是相当强大的多媒体编解码框架,在深入分析其源代码之前必须要有基本的多媒体基础知识,否则其源代码会非常晦涩难懂。本文将从介绍一些基本的多媒体只是,主要是为研读ffmpeg源代码做准...

天下杰论
2015/04/22
0
0
Twitch如何实现转码比FFmpeg性能提升65%?(下)

文 / Jeff Gong, Sahil Dhanju, Chih-Chiang Lu, Yueshi Shen 编者按:超过220万创作者在Twitch发布海量的视频,这对实时转码业务造成了巨大压力,Twitch团队通过优化多线程的转码服务以及I...

livevideostack
2017/12/13
0
0
通过FFmpeg解码和OpenGL的YUV转RGB实现Android视频播放

前言 在我的博文 https://blog.csdn.net/ericbar/article/details/80506390 中,我们在Android平台上,实现了通过FFmpeg在native(C/C++)层进行视频解码,并通过OpenGL实现了硬件渲染工作,...

ericbar
05/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Confluence 6 Windows 中以服务方式自动重启的原因

针对长时间使用的 Confluence,我们推荐你配置 Confluence 自动随操作系统重启而启动。针对一些 Windows 的服务器,这意味着需要让 Confluence 以服务的方式运行。 有下面 2 种方式来以服务的...

honeymose
33分钟前
1
0
day93-20180920-英语流利阅读-待学习

时尚之觞:外表光鲜靓丽,其实穷得要命 Lala 2018-09-20 1.今日导读 讲到时尚界,我们脑海里浮现的可能都是模特和设计师光鲜靓丽、从容潇洒的模样。可是,最近在法国出版的一本书却颠覆了我们...

飞鱼说编程
51分钟前
3
0
maven的pom.xml用解决版本问题

maven管理库依赖,有个好处就是连同库的依赖的全部jar文件一起下载,免去手工添加的麻烦,但同时也带来了同一个jar会被下载了不同版本的问题,好在pom的配置里面允许用<exclusion>来排除一些...

JAVA码猿
今天
1
0
20180920 rzsz传输文件、用户和用户组相关配置文件与管理

利用rz、sz实现Linux与Windows互传文件 [root@centos01 ~]# yum install -y lrzsz # 安装工具sz test.txt # 弹出对话框,传递到选择的路径下rz # 回车后,会从对话框中选择对应的文件传递...

野雪球
今天
2
0
OSChina 周四乱弹 —— 毒蛇当辣条

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 达尔文:分享花澤香菜/前野智昭/小野大輔/井上喜久子的单曲《ミッション! 健?康?第?イチ》 《ミッション! 健?康?第?イチ》- 花澤香菜/前野智...

小小编辑
今天
312
17

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部