понеділок, 2 червня 2014 р.

FFMBC_03: Outputting to the Screen

(*
    tutorial02.c
    A pedagogical video player that will stream through every video frame as fast as it can.

    This tutorial was written by Stephen Dranger (dranger@gmail.com).

    Code based on FFplay, Copyright (c) 2003 Fabrice Bellard,
    and a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)

    Conversion to Delphi by Oleksandr Nazaruk (mail@freehand.com.ua)
    Tested on Windows 8.1 64bit rus, compiled with Delphi XE5

    Run using

    tutorial01 myvideofile.mpg

    to play the video stream on your screen.
*)

program tutorial02;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows,
  VCL.Graphics,
  SDL2 in '../../../Injest/LIB/SDL/Pascal-SDL-2-Header/SDL2.pas',
  avcodec in '../FFmbc-0.7-rc8/libavcodec/avcodec.pas',
  avformat in '../FFmbc-0.7-rc8/libavformat/avformat.pas',
  avio in '../FFmbc-0.7-rc8/libavformat/avio.pas',
  avutil in '../FFmbc-0.7-rc8/libavutil/avutil.pas',
  opt in '../FFmbc-0.7-rc8/libavutil/opt.pas',
  rational in '../FFmbc-0.7-rc8/libavutil/rational.pas',
  imgutils in '../FFmbc-0.7-rc8/libavutil/imgutils.pas',
  fifo in '../FFmbc-0.7-rc8/libavutil/fifo.pas',
  file_ in '../FFmbc-0.7-rc8/libavutil/file_.pas',
  ctypes in '../FFmbc-0.7-rc8/ctypes.pas',
  swscale in '../FFmbc-0.7-rc8/libswscale/swscale.pas',
  avdevice in '../FFmbc-0.7-rc8/libavdevice/avdevice.pas',
  postprocess in '../FFmbc-0.7-rc8/libpostproc/postprocess.pas';

procedure SaveFrame(pFrameRGB: PAVFrame; width: integer; height: integer; iFrame: integer);
var
  bmp: TBitmap;
  i: integer;
begin
  bmp := TBitmap.Create;
  try
    bmp.PixelFormat := pf32bit;
    bmp.Width := width;
    bmp.Height := height;

    for i := 0 to bmp.Height - 1 do
      CopyMemory ( bmp.ScanLine[i], pointer(integer(pFrameRGB.data[0]) + bmp.Width * 4 * i), bmp.Width * 4);

    bmp.SaveToFile(format('frame_%d.bmp', [iFrame]));
  finally
    bmp.free;
  end;
end;

var
  i, videoStream  : integer;
  src_filename    : ansistring;
  pFormatCtx      : PAVFormatContext = nil;
  pCodecCtx       : PAVCodecContext = nil;
  pCodec          : PAVCodec = nil;
  optionsDict     : PAVDictionary = nil;
  pFrame          : PAVFrame = nil;
  pFrameYUV420P   : PAVFrame = nil;
  packet          : TAVPacket;
  frameFinished   : integer;
  sws_ctx         : PSwsContext = nil;
  numBytes        : integer;
  buffer          : pcuint8;
  screen          : PSDL_Window = nil;
  texture         : PSDL_texture  = nil;
  render          : PSDL_renderer = nil;
  event           : TSDL_Event;
  pict            : TAVPicture;
begin
  try
    if (ParamCount < 1) then
    begin
      writeln('Please provide a movie file');
      exit;
    end;

    src_filename:=(AnsiString(ParamStr(1)));

    if SDL_Init(SDL_INIT_VIDEO or SDL_INIT_AUDIO or SDL_INIT_TIMER)<0 then
    begin
      writeln(format('Could not initialize SDL - %s', [SDL_GetError()]));
      exit;
    end;

    // Register all formats and codecs
    av_register_all();

    // Open video file
    if (avformat_open_input(@pFormatCtx, PAnsiChar(src_filename), nil, nil)<>0) then
    begin
      writeln(format('Could not open source file %s', [src_filename]));
      exit;
    end;

    // Retrieve stream information
    if avformat_find_stream_info(pFormatCtx , nil) < 0 then
    begin
      writeln(format('Could not find stream information', []));
      exit;
    end;

    // Dump information about file onto standard error
    av_dump_format(pFormatCtx, 0, PAnsiChar(src_filename), 0);

    // Find the first video stream
    videoStream:=-1;
    for i:=0 to pFormatCtx.nb_streams-1 do
    begin
      if pFormatCtx.streams^.codec.codec_type =  AVMEDIA_TYPE_VIDEO then
      begin
        videoStream := i;
        // Get a pointer to the codec context for the video stream
        pCodecCtx:=pFormatCtx.streams^.codec;
        break;
      end;
      inc(pFormatCtx.streams);
    end;

    if videoStream=-1 then
    begin
      writeln('Didn''t find a video stream');
      exit;
    end;

    // Find the decoder for the video stream
    pCodec:=avcodec_find_decoder(pCodecCtx.codec_id);
    if not assigned(pCodec) then
    begin
      writeln('Unsupported codec!');
      exit;
    end;

    // Open codec
    if avcodec_open2(pCodecCtx, pCodec, @optionsDict)<0 then
    begin
      writeln('Could not open codec');
      exit;
    end;

    // Allocate video frame
    pFrame:=avcodec_alloc_frame;


    // Allocate an AVFrame structure
    pFrameYUV420P:=avcodec_alloc_frame();
    if not assigned(pFrameYUV420P) then
    begin
      writeln('Could not Allocate AVFrame structure');
      exit;
    end;

    // Determine required buffer size and allocate buffer
    numBytes:=avpicture_get_size(pCodecCtx.pix_fmt, pCodecCtx.width, pCodecCtx.height);
    buffer:=av_malloc(numBytes*sizeof(cuint));

    sws_ctx :=
    sws_getContext
    (
        pCodecCtx.width,
        pCodecCtx.height,
        pCodecCtx.pix_fmt,
        pCodecCtx.width,
        pCodecCtx.height,
        PIX_FMT_YUV420P,
        SWS_BILINEAR,
        nil,
        nil,
        nil
    );

    // Assign appropriate parts of buffer to image planes in pFrameYUV420P
    // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
    // of AVPicture
    avpicture_fill(PAVPicture(pFrameYUV420P), buffer, PIX_FMT_YUV420P, pCodecCtx.width, pCodecCtx.height);



    // Make a screen to put our video
    screen:=SDL_CreateWindow('Tutorial_02', SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, pCodecCtx.width, pCodecCtx.height, SDL_WINDOW_SHOWN );
    if not assigned(screen) then
    begin
      writeln('SDL: could CreateWindow');
      exit;
    end;

    render := SDL_CreateRenderer(screen, -1, 0);

    // Allocate a place to put our YUV image on that screen
    texture:=SDL_CreateTexture(render, SDL_PIXELFORMAT_YV12, sint32(SDL_TEXTUREACCESS_STREAMING), pCodecCtx.width, pCodecCtx.height);


    // Read frames and save first five frames to disk
    i:=0;
    while(av_read_frame(pFormatCtx, packet)>=0) do
    begin
      // Is this a packet from the video stream?
      if(packet.stream_index=videoStream) then
      begin
        // Decode video frame
        avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, @packet);

        // Did we get a video frame?
        if frameFinished>0 then
        begin

         pict.data[0] := pFrameYUV420P.data[0];
         pict.data[1] := pFrameYUV420P.data[2];
         pict.data[2] := pFrameYUV420P.data[1];

         pict.linesize[0] := pFrameYUV420P.linesize[0];
         pict.linesize[1] := pFrameYUV420P.linesize[2];
         pict.linesize[2] := pFrameYUV420P.linesize[1];


         // Convert the image into YUV format that SDL uses
          sws_scale
          (
            sws_ctx,
            @pFrame.data,
            @pFrame.linesize,
            0,
            pCodecCtx.height,
            @pict.data,
            @pict.linesize
          );


          SDL_UpdateTexture(texture, nil, buffer, pCodecCtx.width);
          SDL_RenderClear(Render);
          SDL_RenderCopy(Render, texture, nil, nil);
          SDL_RenderPresent(Render);
        end;
      end;
      // Free the packet that was allocated by av_read_frame
      av_free_packet(@packet);

      SDL_PollEvent(@event);
      case event.type_ of
        SDL_QUITEV:
        begin
          SDL_Quit();
          exit;
        end;
      end;
    end;

    // Free the pFrameYUV420P image
    av_free(buffer);
    av_free(pFrameYUV420P);

    // Free the YUV frame
    av_free(pFrame);

    // Close the codec
    avcodec_close(pCodecCtx);

    if assigned(texture) then
    begin
      SDL_DestroyRenderer(texture);
      texture := nil;
    end;

    if assigned(Render) then
    begin
      SDL_DestroyRenderer(Render);
      Render := nil;
    end;

    if assigned(screen) then
    begin
      SDL_DestroyWindow(screen);
      screen := nil;
    end;

    // Close the video file
    av_close_input_file(pFormatCtx);

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Stand: 
  • Windows 8.1 Enterprise 64Bit Rus
  • Delphi XE5
Links: 
  • Dranger tutorial02 is ffmpeg and SDL Tutorial or How to Write a Video Player in Less Than 1000 Lines

Немає коментарів:

Дописати коментар