ChinaFFmpeg

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 13368|回复: 2

[MacOS] 使用 FFmpeg 开发播放器基础 (一)

[复制链接]
发表于 2014-6-2 11:10:51 | 显示全部楼层 |阅读模式


使用 FFmpeg 开发播放器基础
第一节 使用 ffmpeg 解码视频文件

使用 ffmpeg 解码多媒体文件之前,首先需要了解一些基本的概念:
容器:多媒体文件中包含视频与音频部分,将音频与视频封装在容器内,封装的形式为容器标准中所规定的格式,除了音频编码数据,视频编码数据,文字编码数据以外,还有一些用来格式化这些数据的字段,组成了容器;
编码器:多媒体文件中包含了视频编码部分与音频编码部分,都为容器所包含;

了解了以上基本概念后,下面看一下解码视频文件的基本步骤:

  • [AppleScript] 纯文本查看 复制代码
    1. 打开视频文件
    2. 从视频文件中打开视频流
    3. 从视频流中读取视频信息到视频帧里面
    4. 如果读取视频真没有完成,就继续从视频流中读取视频信息到视频帧里面
    5. 对视频帧进行操作,例如解码,绘制到屏幕,保存文件等
    6. 继续从视频流中读取视频信息到视频帧里面

在执行第2步之前,首先要注意,如果希望使用 ffmpeg 第一步要注册 ffmpeg 的函数,注册完成以后才能够顺利的使用 ffmpeg;
[AppleScript] 纯文本查看 复制代码
av_register_all();
通过这个函数,将ffmpeg中的avformat/avcodec/avfilter等模块全部注册完成了;
接下来就可以打开视频文件;
[AppleScript] 纯文本查看 复制代码
avformat_open_input();
通过该函数打开视频文件,分析对应的文件的信息,将信息填充进AVFormatContext中,填充完毕以后,可以获得视频流信息;
[AppleScript] 纯文本查看 复制代码
av_find_stream_info();
获得到了视频信息之后,可以知道视频中含多少路视频流,一路视频流用一路视频流的解流方式进行解流,例如音频流,例如视频流,例如字幕流等这里只讲述视频流;当或得到视频流信息后,既可根据对应的视频流的类型进行解码,例如h264,则会根据查找对应的解码器进行获得解码器的信息;
[AppleScript] 纯文本查看 复制代码
avcodec_find_decoder();
或得到解码器信息后,则可以将解码器打开;
[AppleScript] 纯文本查看 复制代码
avcodec_open2();
打开解码器以后就可以根据解码信息将对应的frame数据进行读取,读取的时候,每一个frame都需要右地方存储,所以,需要申请frame内存空间;
[AppleScript] 纯文本查看 复制代码
av_frame_alloc();
申请完成之后则开始进行了读取视频frame,然后解frame操作;
[AppleScript] 纯文本查看 复制代码
av_read_frame();
avcodec_decode_video2();
av_read_frame会将数据从format中读取出来存储入packet中,然后使用avcodec_decode_video来对packet的data进行解码,然后将对应的解码后的数据存入frame中,frame中的数据可以存储入文件中,也可以直接绘制到屏幕上;
将数据存储入文件中的代码实例如下:
[AppleScript] 纯文本查看 复制代码
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <stdio.h>

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 = NULL;
  int             i = 0;
  int             videoStream = 0;
  AVCodecContext  *pCodecCtx = NULL;
  AVCodec         *pCodec = NULL;
  AVFrame         *pFrame = NULL; 
  AVFrame         *pFrameRGB = NULL;
  AVPacket        packet;
  int             frameFinished;
  int             numBytes;
  uint8_t         *buffer = NULL;

  AVDictionary    *optionsDict = NULL;
  struct SwsContext      *sws_ctx = NULL;
  if(argc < 2) {
    printf("Please provide a movie file\n");
    return -1;
  }
  // Register all formats and codecs
  av_register_all();
  // Open video file
  if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
    return -1; // Couldn't open file
  // Retrieve stream information
  if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
    return -1; // Couldn't find stream information
  // Dump information about file onto standard error
  av_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 -1; // 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 -1; // Codec not found
  }
  // Open codec
  if(avcodec_open2(pCodecCtx, pCodec, &optionsDict) < 0)
    return -1; // Could not open codec
  // Allocate video frame
  pFrame = av_frame_alloc();
  // Allocate an AVFrame structure
  pFrameRGB = av_frame_alloc();
  if(pFrameRGB == NULL)
    return -1;
  // 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));

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

  // 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 from its native format to RGB
        sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

// Save the frame to disk
i++;
        SaveFrame(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
  avformat_close_input(&pFormatCtx);
  return 0;
} 

在OSX下面编译如下:


code对应的编译方法如下:


[AppleScript] 纯文本查看 复制代码
gcc -Wall -ggdb test.c  -I/usr/include/SDL/ -c -o test.o
gcc -Wall -ggdb test.o -L/usr/local/lib -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -framework QTKit -framework Foundation -framework QuartzCore -framework CoreVideo -framework Foundation -framework AVFoundation -framework CoreMedia -framework CoreFoundation -framework VideoDecodeAcceleration -framework QuartzCore -liconv -L/usr//lib -lSDLmain -lSDL -Wl,-framework,Cocoa -lm -lbz2 -lz -pthread -o test

Linux下编译更为简单:
[AppleScript] 纯文本查看 复制代码
gcc -Wall -ggdb test.c  -I/usr/include/SDL/ -c -o test.o
gcc -Wall -ggdb test.o -o test -L/usr/local/lib -lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavuti -lm -lbz2 -lz -pthread 


ffmpeg版本如下:

[AppleScript] 纯文本查看 复制代码
ffmpeg version N-63027-g9e58677 Copyright (c) 2000-2014 the FFmpeg developers
  built on Jun  1 2014 17:05:37 with Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
  configuration: --prefix=/usr/local/
  libavutil      52. 81.100 / 52. 81.100
  libavcodec     55. 60.103 / 55. 60.103
  libavformat    55. 37.102 / 55. 37.102
  libavdevice    55. 13.101 / 55. 13.101
  libavfilter     4.  5.100 /  4.  5.100
  libswscale      2.  6.100 /  2.  6.100
  libswresample   0. 18.100 /  0. 18.100


回复

使用道具 举报

发表于 2014-6-2 11:17:07 | 显示全部楼层
我来了~~~~嚎嚎嚎嚎

                  大师兄出品, 必属精品


                  强烈顶起
回复 支持 反对

使用道具 举报

发表于 2020-4-14 22:34:19 | 显示全部楼层
是时候开堂视频的收费课程了!!!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|Archiver|ChinaFFmpeg

GMT+8, 2024-12-27 06:35 , Processed in 0.069654 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表