从零开始学习音视频编程技术(五) 使用FFMPEG解码视频之保存成图片

xiaoxiao2021-02-28  122

首先来个简单的例子,使用FFMPEG打开视频文件,并解码保存成一张张的图片。

具体的步骤如下所示:

1.首先需要先初始化一下,使用如下函数:

1 av_register_all();  //初始化FFMPEG  调用了这个才能正常适用编码器和解码器

使用这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用,否则会在打开编解码器的时候失败。

2.接着需要分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行

1 AVFormatContext *pFormatCtx = avformat_alloc_context();

3.接着调用打开视频文件

这里文件名先不要使用中文,否则会打开失败,后期再讲解如何处理中文。

1 2 char  *file_path =  "E:in.mp4" ; avformat_open_input(&pFormatCtx, file_path, NULL, NULL);

4.文件打开成功后就是查找文件中的视频流了:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15      ///循环查找视频中包含的流信息,直到找到视频类型的流          ///便将其记录下来 保存到videoStream变量中      ///这里我们现在只处理视频流  音频流先不管他      for  (i = 0; i < pFormatCtx->nb_streams; i++) {          if  (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {              videoStream = i;          }      }        ///如果videoStream为-1 说明没有找到视频流      if  (videoStream == -1) {          printf ("Didn't find a video stream. ");          return  -1;      }

5.现在根据视频流  打开一个解码器来解码:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16      ///查找解码器          pCodecCtx = pFormatCtx->streams[videoStream]->codec;      pCodec = avcodec_find_decoder(pCodecCtx->codec_id);        if  (pCodec == NULL) {          printf ("Codec not found. ");          return  -1;      }        ///打开解码器      if  (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {          printf ("Could not open codec. ");          return  -1;      }

可以看出  我们可以直接根据查找到的视频流信息获取到解码器。

而且我们并不知道他实际用的是什么编码器。

这就是为什么一开始我们使用FFMPEG来操作,因为很多东西我们可以不关系。

6.现在开始读取视频了:

1 2 3 4 5 6 7 8      int  y_size = pCodecCtx->width * pCodecCtx->height;      AVPacket *packet = (AVPacket *)  malloc ( sizeof (AVPacket));  //分配一个packet      av_new_packet(packet, y_size);  //分配packet的数据        if  (av_read_frame(pFormatCtx, packet) < 0)      {          break ;  //这里认为视频读取完了      }

可以看出 av_read_frame读取的是一帧视频,并存入一个AVPacket的结构中。

7.前面我们说过 视频里面的数据是经过编码压缩的,因此这里我们需要将其解码:

1 2 3 4 5 6 7 8 9 10      if  (packet->stream_index == videoStream)       {                  ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);            if  (ret < 0) {              printf ("decode error. ");              return  -1;          }      }

8.基本上所有解码器解码之后得到的图像数据都是YUV420的格式,而这里我们需要将其保存成图片文件,因此需要将得到的YUV420数据转换成RGB格式,转换格式也是直接使用FFMPEG来完成:

1 2 3 4 5 6      if  (got_picture) {                  sws_scale(img_convert_ctx,                  (uint8_t  const  *  const  *) pFrame->data,                  pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,                  pFrameRGB->linesize);      }

至于YUV420和RGB图像格式的具体内容,这里不用去了解。这里只需要知道有这么个东西就行了,对我们使用FFMPEG转换没有影响。

9.得到RGB数据之后就是直接写入文件了:

1      SaveFrame(pFrameRGB,     pCodecCtx->width,pCodecCtx->height,index++);  //保存图片     if (index > 50) return 0; //这里我们就保存50张图片

至此读取视频解码保存成图片就写好了:

完整的代码如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 extern  "C" {      #include  "libavcodec/avcodec.h"      #include  "libavformat/avformat.h"      #include  "libavutil/pixfmt.h"      #include  "libswscale/swscale.h" }   #include <stdio.h>   ///现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。 ///我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。 void  SaveFrame(AVFrame *pFrame,  int  width,  int  height, int  index) {      FILE  *pFile;    char  szFilename[32];    int   y;      // Open file    sprintf (szFilename,  "frame%d.ppm" , index);    pFile= fopen (szFilename,  "wb" );      if (pFile==NULL)      return ;      // Write header    fprintf (pFile, "P6 %d %d 255 ", width, height);      // Write pixel data    for (y=0; y<height; y++)    {      fwrite (pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);    }      // Close file    fclose (pFile);   }     int  main( int  argc,  char  *argv[]) {      char  *file_path =  "E:in.mp4" ;        AVFormatContext *pFormatCtx;      AVCodecContext *pCodecCtx;      AVCodec *pCodec;      AVFrame *pFrame, *pFrameRGB;      AVPacket *packet;      uint8_t *out_buffer;        static  struct  SwsContext *img_convert_ctx;        int  videoStream, i, numBytes;      int  ret, got_picture;        av_register_all();  //初始化FFMPEG  调用了这个才能正常适用编码器和解码器        //Allocate an AVFormatContext.      pFormatCtx = avformat_alloc_context();        if  (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) {          printf ("can't open the file.  ");          return  -1;      }        if  (avformat_find_stream_info(pFormatCtx, NULL) < 0) {          printf ("Could't find stream infomation. ");          return  -1;      }        videoStream = -1;        ///循环查找视频中包含的流信息,直到找到视频类型的流      ///便将其记录下来 保存到videoStream变量中      ///这里我们现在只处理视频流  音频流先不管他      for  (i = 0; i < pFormatCtx->nb_streams; i++) {          if  (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {              videoStream = i;          }      }        ///如果videoStream为-1 说明没有找到视频流      if  (videoStream == -1) {          printf ("Didn't find a video stream. ");          return  -1;      }        ///查找解码器      pCodecCtx = pFormatCtx->streams[videoStream]->codec;      pCodec = avcodec_find_decoder(pCodecCtx->codec_id);        if  (pCodec == NULL) {          printf ("Codec not found. ");          return  -1;      }        ///打开解码器      if  (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {          printf ("Could not open codec. ");          return  -1;      }        pFrame = av_frame_alloc();      pFrameRGB = av_frame_alloc();        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,              pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,              PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);        numBytes = avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height);        out_buffer = (uint8_t *) av_malloc(numBytes *  sizeof (uint8_t));      avpicture_fill((AVPicture *) pFrameRGB, out_buffer, PIX_FMT_RGB24,              pCodecCtx->width, pCodecCtx->height);        int  y_size = pCodecCtx->width * pCodecCtx->height;        packet = (AVPacket *)  malloc ( sizeof (AVPacket));  //分配一个packet      av_new_packet(packet, y_size);  //分配packet的数据        av_dump_format(pFormatCtx, 0, file_path, 0);  //输出视频信息        int  index = 0;        while  (1)      {          if  (av_read_frame(pFormatCtx, packet) < 0)          {              break ;  //这里认为视频读取完了          }            if  (packet->stream_index == videoStream) {              ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);                if  (ret < 0) {                  printf ("decode error. ");                  return  -1;              }                if  (got_picture) {                  sws_scale(img_convert_ctx,                          (uint8_t  const  *  const  *) pFrame->data,                          pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,                          pFrameRGB->linesize);                    SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++);  //保存图片                  if  (index > 50)  return  0;  //这里我们就保存50张图片              }          }          av_free_packet(packet);      }      av_free(out_buffer);      av_free(pFrameRGB);      avcodec_close(pCodecCtx);      avformat_close_input(&pFormatCtx);        return  0; }

完整的工程下载:

http://download.csdn.net/detail/qq214517703/9623516

转载请注明原文地址: https://www.6miu.com/read-42721.html

最新回复(0)