(*
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
Немає коментарів:
Дописати коментар