zoukankan      html  css  js  c++  java
  • ffmpeg简单视频播放器

    参考学习雷霄骅的最简单的基于FFMPEG视频播放器。

    在移植代码到我的linux环境中时总出现问题,所以干脆把雷神的代码从头到尾分析并移植过来调试。

    开发环境:

      操作系统:ubuntu14

      ffmpeg版本:3.2.2

      sdl版本:2

    代码我是自己参照雷神的代码敲的,在这过程中,我发现写代码还是要加打印,没有什么能一拿过来就可以成功运行。尤其是在自己设计接口的时候,返回值最好是返回运行结果,需要传出的内存通过参数来传出。

    对于错误的判断打印还是需要详细一些,以后在写比较大的程序时要养成加log的习惯,这对调试程序还是有很大帮助的。

    下面插入代码:

    说明:该程序运行一段时间后会变成黑白,具体原因再查

    编译与运行:

    gcc -g main.c -o test -I /usr/local/ffmpeg/include -L /usr/local/ffmpeg/lib -lavutil -lavformat -lavcodec -lz -lavutil -lswscale -L /usr/lib/x86_64-linux-gnu -lSDL2 -lSDL2main
    (其中路径按照自己的安装路径来 -g 是加GDB调试)

    ./test

      1 #ifdef _cplusplus
      2 extern "C"
      3 {
      4 #endif
      5 
      6 #include<stdio.h>
      7 #include<libavcodec/avcodec.h>
      8 #include<libavformat/avformat.h>
      9 #include<libavutil/avutil.h>
     10 #include<libswscale/swscale.h>
     11 #include<libavutil/avutil.h>
     12 #include<libavutil/imgutils.h>
     13 #include<SDL2/SDL.h>
     14 
     15 //是否将YUV420P内容输出到文件
     16 #define OUTPUT_YUV420P 0
     17 //要播放的文件路径
     18 #define filename "/home/sns/cuc_ieschool.flv"
     19 //要输出YUV420P内容的文件路径
     20 #define outfilename "/home/sns/output.yuv"
     21 
     22 int main(int argc, char **argv)
     23 {
     24     //变量定义*********************************************************************
     25     AVFormatContext *pFormatCtx;
     26     int i, videoStream;
     27     AVCodecContext *pCodecCtx;
     28     AVCodec *pCodec;
     29     AVFrame *pFrame;
     30     AVFrame *pFrameYUV;
     31     uint8_t *buffer;
     32     int numBytes;
     33 
     34     SDL_Window *screen;
     35     SDL_Renderer *sdlRender;
     36     SDL_Texture *sdlTexture;
     37     SDL_Rect sdlRect;
     38     int frameFinished;
     39     AVPacket packet;
     40     i = 0;
     41     struct SwsContext *img_convert_ctx;
     42     int err_code;
     43     char buf[1024];
     44     FILE *fp_yuv;
     45     int y_size;
     46     //*******************************************************************************
     47     av_register_all();
     48     //1、打开视频文件*************************************************
     49     pFormatCtx = avformat_alloc_context();
     50     err_code = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
     51     if (err_code != 0)
     52     {//打开文件失败
     53         av_strerror(err_code, buf, 1024);
     54         printf("coundn't open the file!,error code = %d(%s)
    ", err_code, buf);
     55         return -1;
     56     }
     57     if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
     58     {
     59         printf("Couldn't find stream information.
    ");
     60         return -1;
     61     }
     62     //2、找到第一个视频流****************************
     63     videoStream = -1;
     64     for (i = 0; i < pFormatCtx->nb_streams; i++)
     65     {
     66         if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
     67         {
     68             videoStream = i;//得到视频流的索引
     69             break;
     70         }
     71     }
     72     if (videoStream == -1)
     73     {
     74         printf("Didn't find a video stream.
    ");
     75         return -1;
     76     }
     77     /* 3、从视频流中得到一个编解码上下文,里面包含了编解码器的所有信息和一个
     78     指向真正的编解码器     ,然后我们找到这个解码器*/
     79     pCodecCtx = pFormatCtx->streams[videoStream]->codec;
     80     pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
     81     if (pCodec == NULL)
     82     {
     83         fprintf(stderr, "Unsupported codec !
    ");
     84         return -1;
     85     }
     86     //4、打开该编解码器
     87     if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
     88     {
     89         printf("cann't open the codec!
    ");
     90         return -1;
     91     }
     92     //5、分配两个视频帧,一个保存得到的原始视频帧,一个保存为指定格式的视频帧(该帧通过原始帧转换得来)
     93     pFrame = av_frame_alloc();
     94     if (pFrame == NULL)
     95     {
     96         printf("pFrame alloc fail!
    ");
     97         return -1;
     98     }
     99     pFrameYUV = av_frame_alloc();
    100     if (pFrameYUV == NULL)
    101     {
    102         printf("pFrameYUV alloc fail!
    ");
    103         return -1;
    104     }
    105     //6、得到一帧视频截图的内存大小并分配内存,并将YUV数据填充进去
    106     numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width,
    107             pCodecCtx->height,1);
    108     buffer = (uint8_t*) av_mallocz(numBytes * sizeof(uint8_t));
    109     if (!buffer)
    110     {
    111         printf("numBytes :%d , buffer malloc 's mem 
    ", numBytes);
    112         return -1;
    113     }
    114     //打印信息
    115     printf("--------------- File Information ----------------
    ");
    116     av_dump_format(pFormatCtx, 0, filename, 0);
    117     printf("-------------------------------------------------
    ");
    118     av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,buffer,
    119             AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
    120     //7、得到指定转换格式的上下文**********************************
    121     img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
    122             pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
    123             AV_PIX_FMT_YUV420P,
    124             SWS_BICUBIC,
    125             NULL, NULL, NULL);
    126     if (img_convert_ctx == NULL)
    127     {
    128         fprintf(stderr, "Cannot initialize the conversion context!
    ");
    129         return -1;
    130     }
    131     //***********************************************************
    132 #if OUTPUT_YUV420P
    133     fp_yuv = fopen(outfilename, "wb+");
    134 #endif
    135     //8、SDL初始化和创建多重windows等准备工作
    136     if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER | SDL_INIT_VIDEO))
    137     {
    138         fprintf(stderr, "Could not initialize SDL - %s
    ", SDL_GetError());
    139         return -1;
    140     }
    141     //使用SDL_CreateWindow代替SDL_SetVideoMode
    142     //创建一个给定高度和宽度、位置和标示的windows。
    143     screen = SDL_CreateWindow("Simplest ffmpeg player's Window",
    144     SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx->width,
    145             pCodecCtx->height, SDL_WINDOW_OPENGL);
    146     if (!screen)
    147     {
    148         fprintf(stderr, "SDL: could not create window - exiting - %s
    ",SDL_GetError());
    149         return -1;
    150     }
    151     //对该window创建一个2D渲染上下文
    152     sdlRender = SDL_CreateRenderer(screen, -1, 0);
    153     if (!sdlRender)
    154     {
    155         fprintf(stderr, "SDL:cound not create render :    %s
    ", SDL_GetError());
    156         return -1;
    157     }
    158     //Create a texture for a rendering context.
    159     //为一个渲染上下文创建一个纹理
    160     //IYUV: Y + U + V  (3 planes)
    161     //YV12: Y + V + U  (3 planes)
    162     sdlTexture = SDL_CreateTexture(sdlRender, SDL_PIXELFORMAT_IYUV,
    163             SDL_TEXTUREACCESS_STREAMING, pCodecCtx->width, pCodecCtx->height);
    164     if (!sdlTexture)
    165     {
    166         fprintf(stderr, "SDL:cound not create Texture :    %s
    ", SDL_GetError());
    167         return -1;
    168     }
    169     //建立一个矩形变量,提供后面使用
    170     sdlRect.x = 0;
    171     sdlRect.y = 0;
    172     sdlRect.w = pCodecCtx->width;
    173     sdlRect.h = pCodecCtx->height;
    174     //9、正式开始读取数据*****************************************
    175     while (av_read_frame(pFormatCtx, &packet) >= 0)
    176     {
    177         //如果读取的包来自视频流
    178         if (packet.stream_index == videoStream)
    179         {
    180             //从包中得到解码后的帧
    181             if (avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,&packet) < 0)
    182             {
    183                 printf("Decode Error!
    ");
    184                 return -1;
    185             }
    186             //如果确定完成得到该视频帧
    187             if (frameFinished)
    188             {
    189                 //转换帧数据格式
    190                 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0,
    191                         pCodecCtx->height,
    192                         pFrameYUV->data,
    193                         pFrameYUV->linesize);
    194 #if OUTPUT_YUV420P
    195                 y_size = pCodecCtx->width * pCodecCtx->height;
    196                 fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);    //Y
    197                 fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U
    198                 fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V
    199 #endif
    200                 //SDL显示~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    201 #if 0
    202                 SDL_UpdateTexture(sdlTexture,NULL,pFrameYUV->data[0],pFrameYUV->linesize[0]);
    203 #else
    204                 SDL_UpdateYUVTexture(sdlTexture, &sdlRect, pFrameYUV->data[0],
    205                         pFrameYUV->linesize[0], pFrameYUV->data[1],
    206                         pFrameYUV->linesize[1], pFrameYUV->data[2],
    207                         pFrameYUV->linesize[2]);
    208 #endif
    209                 SDL_RenderClear(sdlRender);
    210                 SDL_RenderCopy(sdlRender, sdlTexture, NULL, &sdlRect);
    211                 SDL_RenderPresent(sdlRender);
    212                 //结束SDL~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    213                 SDL_Delay(40);//延时
    214             }
    215         }
    216         av_free_packet(&packet);//释放读出来的包
    217     }
    218     //**************************************************************************************
    219     //10、释放分配的内存或关闭文件等操作
    220 #if OUTPUT_YUV420P
    221     fclose(fp_yuv);
    222 #endif
    223     sws_freeContext(img_convert_ctx);
    224     SDL_Quit();
    225     av_free(buffer);
    226     av_free(pFrame);
    227     av_free(pFrameYUV);
    228     avcodec_close(pCodecCtx);
    229     avformat_close_input(&pFormatCtx);
    230     return EXIT_SUCCESS;
    231 }
    232 
    233 #ifdef _cplusplus
    234 }
    235 #endif
  • 相关阅读:
    将aspx页面编译成dll
    Jquery 验证数字
    c#反编译生成DLL过程
    c#进制转换
    Spring Mvc 实例
    wamp phpMyAdmin error #1045
    Tomcat相关知识点总结(jsp)
    Java ---学习笔记(泛型)
    Java IO ---学习笔记(文件操作与随机访问文件)
    Java IO ---学习笔记(字符流)
  • 原文地址:https://www.cnblogs.com/xishuichangliu/p/6344229.html
Copyright © 2011-2022 走看看