mirror of
https://github.com/Laex/Delphi-OpenCV.git
synced 2024-11-18 01:05:53 +01:00
1416554bdb
ml.pas ffmpeg Signed-off-by: Laex <laex@bk.ru>
241 lines
6.5 KiB
ObjectPascal
241 lines
6.5 KiB
ObjectPascal
program avcodec_sample;
|
|
|
|
{$APPTYPE CONSOLE}
|
|
{$R *.res}
|
|
{$include ffmpeg.inc}
|
|
|
|
uses
|
|
Winapi.Windows,
|
|
System.SysUtils,
|
|
System.Classes,
|
|
ffmpeglib in '..\..\ffmpeglib.pas',
|
|
ctypes in '..\..\..\ctypes\ctypes.pas',
|
|
avformat in '..\..\libavformat\avformat.pas',
|
|
avio in '..\..\libavformat\avio.pas',
|
|
avutil in '..\..\libavutil\avutil.pas',
|
|
buffer in '..\..\libavutil\buffer.pas',
|
|
dict in '..\..\libavutil\dict.pas',
|
|
frame in '..\..\libavutil\frame.pas',
|
|
log in '..\..\libavutil\log.pas',
|
|
opt in '..\..\libavutil\opt.pas',
|
|
pixfmt in '..\..\libavutil\pixfmt.pas',
|
|
rational in '..\..\libavutil\rational.pas',
|
|
samplefmt in '..\..\libavutil\samplefmt.pas',
|
|
parseutils in '..\..\libavutil\parseutils.pas',
|
|
swscale in '..\..\libswscale\swscale.pas',
|
|
pixdesc in '..\..\libavutil\pixdesc.pas',
|
|
imgutils in '..\..\libavutil\imgutils.pas',
|
|
mem in '..\..\libavutil\mem.pas',
|
|
error in '..\..\libavutil\error.pas',
|
|
avfilter in '..\..\libavfilter\avfilter.pas',
|
|
buffersink in '..\..\libavfilter\buffersink.pas',
|
|
mathematics in '..\..\libavutil\mathematics.pas',
|
|
libavcodec.avcodec in '..\..\libavcodec\libavcodec.avcodec.pas',
|
|
buffersrc in '..\..\libavfilter\buffersrc.pas',
|
|
errno in '..\..\libavutil\errno.pas';
|
|
|
|
function GetNextFrame(pFormatCtx: pAVFormatContext; pCodecCtx: pAVCodecContext; videoStream: Integer; pFrame: pAVFrame): Boolean;
|
|
|
|
label
|
|
loop_exit;
|
|
|
|
Var
|
|
packet: TAVPacket;
|
|
bytesRemaining: Integer;
|
|
rawData: pByte;
|
|
fFirstTime: Boolean;
|
|
bytesDecoded: Integer;
|
|
frameFinished: Integer;
|
|
begin
|
|
bytesRemaining := 0;
|
|
fFirstTime := true;
|
|
// First time we're called, set packet.data to nil to indicate it
|
|
// doesn't have to be freed
|
|
if (fFirstTime) then
|
|
begin
|
|
fFirstTime := false;
|
|
packet.data := nil;
|
|
end;
|
|
|
|
// Decode packets until we have decoded a complete frame
|
|
while (true) do
|
|
begin
|
|
// Work on the current packet until we have decoded all of it
|
|
while (bytesRemaining > 0) do
|
|
begin
|
|
// Decode the next chunk of data
|
|
bytesDecoded := avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, @packet);
|
|
|
|
// Was there an error?
|
|
if (bytesDecoded < 0) then
|
|
begin
|
|
Writeln('Error while decoding frame');
|
|
Exit(false);
|
|
end;
|
|
|
|
bytesRemaining := bytesRemaining - bytesDecoded;
|
|
rawData := rawData + bytesDecoded;
|
|
|
|
// Did we finish the current frame? Then we can return
|
|
if (frameFinished = 0) then
|
|
Exit(true);
|
|
end;
|
|
|
|
// Read the next packet, skipping all packets that aren't for this
|
|
// stream
|
|
repeat
|
|
// Free old packet
|
|
if Assigned(packet.data) then
|
|
av_free_packet(packet);
|
|
|
|
// Read new packet
|
|
if (av_read_frame(pFormatCtx, packet) < 0) then
|
|
goto loop_exit;
|
|
until (packet.stream_index = videoStream);
|
|
|
|
bytesRemaining := packet.size;
|
|
rawData := packet.data;
|
|
end;
|
|
|
|
loop_exit:
|
|
|
|
// Decode the rest of the last frame
|
|
bytesDecoded := avcodec_decode_video2(pCodecCtx, pFrame, frameFinished, @packet);
|
|
|
|
// Free last packet
|
|
if Assigned(packet.data) then
|
|
av_free_packet(packet);
|
|
|
|
Result := frameFinished <> 0;
|
|
end;
|
|
|
|
procedure SaveFrame(pFrame: pAVFrame; width: Integer; height: Integer; iFrame: Integer);
|
|
Var
|
|
M: TFileStream;
|
|
y: Integer;
|
|
A: AnsiString;
|
|
begin
|
|
// Open file
|
|
M := TFileStream.Create(Format('frame%d.ppm', [iFrame]), fmCreate);
|
|
// Write header
|
|
A := Format('P6'#13#10'%d %d'#13#10'255'#13#10, [width, height]);
|
|
M.WriteBuffer((@A[1])^, Length(A));
|
|
|
|
// Write pixel data
|
|
for y := 0 to height - 1 do
|
|
M.Write(Pointer(Integer(@pFrame^.data[0]) + y * pFrame^.linesize[0])^, width * 3);
|
|
// Close file
|
|
M.Free;
|
|
end;
|
|
|
|
Var
|
|
pFormatCtx: pAVFormatContext;
|
|
i, videoStream: Integer;
|
|
pCodecCtx: pAVCodecContext;
|
|
pCodec: pAVCodec;
|
|
pFrame: pAVFrame;
|
|
pFrameRGB: pAVFrame;
|
|
numBytes: Integer;
|
|
buffer: pByte;
|
|
|
|
function open_input_file(const filename: pAnsiChar): Integer;
|
|
Var
|
|
ret: Integer;
|
|
begin
|
|
ret := avformat_open_input(pFormatCtx, filename, nil, nil);
|
|
if (ret < 0) then
|
|
begin
|
|
// av_log(nil, AV_LOG_ERROR, 'Cannot open input file\n');
|
|
Exit(ret);
|
|
end;
|
|
|
|
ret := avformat_find_stream_info(pFormatCtx, nil);
|
|
if (ret < 0) then
|
|
begin
|
|
// av_log(nil, AV_LOG_ERROR, 'Cannot find stream information\n');
|
|
Exit(ret);
|
|
end;
|
|
|
|
(* select the video stream *)
|
|
ret := av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, pCodec, 0);
|
|
if (ret < 0) then
|
|
begin
|
|
// av_log(nil, AV_LOG_ERROR, 'Cannot find a video stream in the input file\n');
|
|
Exit(ret);
|
|
end;
|
|
videoStream := ret;
|
|
pCodecCtx := pFormatCtx^.streams[videoStream]^.codec;
|
|
|
|
// Inform the codec that we can handle truncated bitstreams -- i.e.,
|
|
// bitstreams where frame boundaries can fall in the middle of packets
|
|
if (pCodec^.capabilities and CODEC_CAP_TRUNCATED) <> 0 then
|
|
pCodecCtx^.flags := pCodecCtx^.flags or CODEC_FLAG_TRUNCATED;
|
|
|
|
(* init the video decoder *)
|
|
ret := avcodec_open2(pCodecCtx, pCodec, nil);
|
|
if (ret < 0) then
|
|
begin
|
|
// av_log(nil, AV_LOG_ERROR, 'Cannot open video decoder\n');
|
|
Exit(ret);
|
|
end;
|
|
|
|
Result := 0;
|
|
end;
|
|
|
|
begin
|
|
try
|
|
|
|
// Register all formats and codecs
|
|
av_register_all();
|
|
|
|
// Open video file
|
|
if open_input_file(pAnsiChar(ParamStr(1))) <> 0 then
|
|
Halt(1); // Couldn't open file
|
|
|
|
// Allocate video frame
|
|
pFrame := avcodec_alloc_frame();
|
|
|
|
// Allocate an AVFrame structure
|
|
pFrameRGB := avcodec_alloc_frame();
|
|
if (pFrameRGB = nil) then
|
|
Halt(1);
|
|
|
|
// Determine required buffer size and allocate buffer
|
|
numBytes := avpicture_get_size(AV_PIX_FMT_RGB24, pCodecCtx^.width, pCodecCtx^.height);
|
|
buffer := AllocMem(numBytes);
|
|
|
|
// Assign appropriate parts of buffer to image planes in pFrameRGB
|
|
avpicture_fill(pAVPicture(pFrameRGB), buffer, AV_PIX_FMT_RGB24, pCodecCtx^.width, pCodecCtx^.height);
|
|
|
|
// Read frames and save first five frames to disk
|
|
i := 0;
|
|
while GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame) do
|
|
begin
|
|
swr_convert(pAVPicture(pFrameRGB), AV_PIX_FMT_RGB24, pFrame, pCodecCtx^.pix_fmt, pCodecCtx^.width, pCodecCtx^.height);
|
|
|
|
// Save the frame to disk
|
|
Inc(i);
|
|
if (i <= 5) then
|
|
SaveFrame(pFrameRGB, pCodecCtx^.width, pCodecCtx^.height, i);
|
|
end;
|
|
|
|
// Free the RGB image
|
|
FreeMem(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);
|
|
|
|
except
|
|
on E: Exception do
|
|
Writeln(E.ClassName, ': ', E.Message);
|
|
end;
|
|
|
|
end.
|