现在的位置: 首页 > 综合 > 正文

ffmpeg 重写tutorial01程序–将一个视频文件解码输出ppm文件或bmp文件

2013年10月05日 ⁄ 综合 ⁄ 共 5914字 ⁄ 字号 评论关闭

原文链接:http://dranger.com/ffmpeg/tutorial01.html

这个链接是一个很好的FFmpeg入门教程,但原文中的代码随着FFmpeg版本不断更新,部分API已经被替换,因此该程序还需要做相应的修改。

参考链接3:BMP文件格式简介

运行环境:window 7 + VS2008 + FFmpeg0.10

关于FFmpeg的编译,我自己编译了一个FFmpeg0.10(当前最新版本)作为测试,生成的lib和dll能用,但生成ffmpeg.exe文件却不能运行,由于时间的关系,我也没有去完整的解决这个问题,网上有专门提供windows下编译好的lib和dll(请参考:http://ffmpeg.zeranoe.com/builds/),初学者在windows下开发时,可以先跳过编译的问题,运行一个可以直接看到的小例子,会让自己更有信心枯燥的编译过程。
自己编译FFmpeg是必不可少了,因为当你需要对FFmpeg的功能进行拓展时(如添加FAAC等),网上未必有人提供相应的lib和dll,正所谓:自助者,天助之, just enjoy it。

我对原代码做了一些小修改,原作者提供的代码是输出.ppm文件,但这种格式实在不好预览,于是我自己写了一个生成.bmp文件的函数,调用了windows.h头文件,关于bmp文件格式及转换,可以参考我提到的“参考链接2”和“参考链接3”。

在编译tutorial01前,你需要确认自己头文件以及lib文件的路径是否正确。如下图,你需要针对lib库的路径做相应的修改。



源代码:
// tutorial01.cpp
// Code based on a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1

// A small sample program that shows how to use libavformat and libavcodec to
// read video from a file.
//
// Use
//
// gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz
//
// to build (assuming libavformat and libavcodec are correctly installed
// your system).
//
// Run using
//
// tutorial01 myvideofile.mpg
//
// to write the first five frames from "myvideofile.mpg" to disk in PPM
// format.
//#include <stdio.h>
extern "C"{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
};

#include <windows.h>

bool saveAsBitmap(AVFrame *pFrameRGB, int width, int height, int iFrame)
{
      FILE *pFile = NULL;
      BITMAPFILEHEADER bmpheader;
      BITMAPINFO bmpinfo;

      char fileName[32];
      int bpp = 24;

      // open file
      sprintf(fileName, "frame%d.bmp", iFrame);
      pFile = fopen(fileName, "wb");
      if (!pFile)
            return false;
      
      bmpheader.bfType = ('M' <<8)|'B';
      bmpheader.bfReserved1 = 0;
      bmpheader.bfReserved2 = 0;
      bmpheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
      bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;

      bmpinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      bmpinfo.bmiHeader.biWidth = width;
      bmpinfo.bmiHeader.biHeight = -height; //reverse the image
      bmpinfo.bmiHeader.biPlanes = 1;
      bmpinfo.bmiHeader.biBitCount = bpp;
      bmpinfo.bmiHeader.biCompression = BI_RGB;
      bmpinfo.bmiHeader.biSizeImage = 0;
      bmpinfo.bmiHeader.biXPelsPerMeter = 100;
      bmpinfo.bmiHeader.biYPelsPerMeter = 100;
      bmpinfo.bmiHeader.biClrUsed = 0;
      bmpinfo.bmiHeader.biClrImportant = 0;

      fwrite(&bmpheader, sizeof(BITMAPFILEHEADER), 1, pFile);
      fwrite(&bmpinfo.bmiHeader, sizeof(BITMAPINFOHEADER), 1, pFile);
      uint8_t *buffer = pFrameRGB->data[0];
      for (int h=0; h<height; h++)
      {
            for (int w=0; w<width; w++)
            {
                  fwrite(buffer+2, 1, 1, pFile);
                  fwrite(buffer+1, 1, 1, pFile);
                  fwrite(buffer, 1, 1, pFile);

                  buffer += 3;
            }
      }
      fclose(pFile);

      return true;
}

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
      FILE *pFile;
      char szFilename[32];
      int  y;
 
      // Open file
      sprintf(szFilename, "frame%d.ppm", iFrame);
      pFile=fopen(szFilename, "wb");
      if(pFile==NULL)
            return;
 
      // Write header
      fprintf(pFile, "P6\n%d %d\n255\n", 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[]) {
      AVFormatContext *pFormatCtx;
      int             i, videoStream;
      AVCodecContext  *pCodecCtx;
      static struct SwsContext *img_convert_ctx;
      AVCodec         *pCodec;
      AVFrame         *pFrame;
      AVFrame         *pFrameRGB;
      AVPacket        packet;
      int             frameFinished;
      int             numBytes;
      uint8_t         *buffer;
 
      if(argc < 2) {
            printf("Please provide a movie file\n");
            return -1;
      }
      // Register all formats and codecs
      av_register_all();
 
      // Open video file
      if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
            return false; // Couldn't open file
 
      // Retrieve stream information
      if(av_find_stream_info(pFormatCtx)<0)
            return false; // Couldn't find stream information
 
      // Dump information about file onto standard error
      dump_format(pFormatCtx, 0, argv[1], 0);
 
      // Find the first video stream
      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 false; // Didn't find a video stream
 
      // Get a pointer to the codec context for the video stream
      pCodecCtx=pFormatCtx->streams[videoStream]->codec;
 
      // Find the decoder for the video stream
      pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
      if(pCodec==NULL) {
            fprintf(stderr, "Unsupported codec!\n");
            return false; // Codec not found
      }
      // Open codec
      if(avcodec_open(pCodecCtx, pCodec)<0)
            return false; // Could not open codec
 
      // Allocate video frame
      pFrame=avcodec_alloc_frame();
 
      // Allocate an AVFrame structure
      pFrameRGB=avcodec_alloc_frame();
      if(pFrameRGB==NULL)
            return false;
 
      // Determine required buffer size and allocate buffer
      numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
      buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
 
      // Assign appropriate parts of buffer to image planes in pFrameRGB
      // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
      // of AVPicture
      avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
                              pCodecCtx->width, pCodecCtx->height);
 
      // Read frames and save first five frames to disk
      i=0;
      while(av_read_frame(pFormatCtx, &packet)>=0) {
            // Is this a packet from the video stream?
            if(packet.stream_index==videoStream) {
            // Decode video frame
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
     
            // Did we get a video frame?
            if(frameFinished) {
                  // Convert the image into RGB format
                  if(img_convert_ctx == NULL) {
                        int w = pCodecCtx->width;
                        int h = pCodecCtx->height;
                        img_convert_ctx = sws_getContext(w, h, pCodecCtx->pix_fmt,
                                          w, h, PIX_FMT_RGB24, SWS_BICUBIC,
                                          NULL, NULL, NULL);
                        if(img_convert_ctx == NULL) {
                              fprintf(stderr, "Cannot initialize the conversion context!\n");
                              return false;
                        }
                  }
                  sws_scale(img_convert_ctx, pFrame->data,
                                    pFrame->linesize, 0,
                                    pCodecCtx->height,
                                    pFrameRGB->data, pFrameRGB->linesize);

                  /*
                  // Convert the image from its native format to RGB
                  img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,
                                          (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width,
                                          pCodecCtx->height);
                  */
                  
                  // Save the frame to disk
                  if (++i <= 10)
                  {
                        //SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
                        saveAsBitmap(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i);
                  } 
            }
      }
   
    // Free the packet that was allocated by av_read_frame
    av_free_packet(&packet);
      }
 
      // Free the RGB image
      av_free(buffer);
      av_free(pFrameRGB);
 
      // Free the YUV frame
      av_free(pFrame);
 
      // Close the codec
      avcodec_close(pCodecCtx);
 
      // Close the video file
      av_close_input_file(pFormatCtx);
 
      return 0;
}

抱歉!评论已关闭.