Skip to content

Commit

Permalink
Support new FFmpeg AVPacket API
Browse files Browse the repository at this point in the history
  • Loading branch information
complexlogic committed Jan 5, 2025
1 parent aea2312 commit 1035863
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 19 deletions.
13 changes: 13 additions & 0 deletions src/lib/ffmpeg-7.0/avcodec.pas
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,18 @@ interface
{$MESSAGE Error 'Linked version of libavcodec is not yet supported!'}
{$IFEND}

{$IF LIBAVCODEC_VERSION_MAJOR < 61}
{$DEFINE OldAVPacketAPI}
{$ENDIF}

const
FF_BUG_AUTODETECT = 1;
type
TAVCodecID = (
AV_CODEC_ID_NONE
);
PAVPacket = ^TAVPacket;
PPAVPacket = ^PAVPacket;
TAVPacket = record
we_do_not_use_buf: pointer;
pts: cint64;
Expand All @@ -70,12 +75,18 @@ TAVPacket = record
we_do_not_use_opaque: pointer;
we_do_not_use_opaque_ref: pointer;
we_do_not_use_time_base: TAVRational;
(* According to the FFmpeg documentation, sizeof(AVPacket) is
* deprecated for the public ABI. However, TAVPacket is still a member of
* TAVStream. So we can't put the incomplete record member because that will
* change the memory layout of TAVStream *)
end;
{$IFDEF OldAVPacketAPI}
PAVPacketList = ^TAVPacketList;
TAVPacketList = record
pkt: TAVPacket;
next: ^TAVPacketList;
end;
{$ENDIF}
PAVCodecDescriptor = ^TAVCodecDescriptor;
TAVCodecDescriptor = record
we_do_not_use_id: TAVCodecID;
Expand Down Expand Up @@ -241,5 +252,7 @@ function avcodec_send_packet(avctx: PAVCodecContext; avpkt: PAVPacket): cint; cd
function avcodec_alloc_context3(codec: PAVCodec): PAVCodecContext; cdecl; external av__codec;
procedure avcodec_free_context(avctx: PPAVCodecContext); cdecl; external av__codec;
function avcodec_parameters_to_context(codec: PAVCodecContext; par: PAVCodecParameters): cint; cdecl; external av__codec;
function av_packet_alloc(): PAVPacket; cdecl; external av__codec;
procedure av_packet_free(pkt: PPAVPacket); cdecl; external av__codec;
implementation
end.
2 changes: 1 addition & 1 deletion src/lib/ffmpeg-7.0/avformat.pas
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function avformat_open_input(ps: PPAVFormatContext; url: PAnsiChar; fmt: PAVInpu
procedure avformat_close_input(s: PPAVFormatContext); cdecl; external av__format;
function avformat_version(): cuint; cdecl; external av__format;
function avformat_find_stream_info(ic: PAVFormatContext; options: PPAVDictionary): cint; cdecl; external av__format;
function av_read_frame(s: PAVFormatContext; var pkt: TAVPacket): cint; cdecl; external av__format;
function av_read_frame(s: PAVFormatContext; pkt: PAVPacket): cint; cdecl; external av__format;
function av_seek_frame(s: PAVFormatContext; stream_index: cint; timestamp: cint64; flags: cint): cint; cdecl; external av__format;
implementation
end.
109 changes: 97 additions & 12 deletions src/media/UAudioDecoder_FFmpeg.pas
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ implementation
{$DEFINE UseFrameDecoderAPI}
{$ENDIF}
{$ENDIF}
{$IF LIBAVCODEC_VERSION_MAJOR < 61}
{$DEFINE OldAVPacketAPI}
{$IFEND}

const
MAX_AUDIOQ_SIZE = (5 * 16 * 1024);
Expand Down Expand Up @@ -153,8 +156,14 @@ TFFmpegDecodeStream = class(TAudioDecodeStream)
fDecoderResumeCond: PSDL_Cond;

// state-vars for DecodeFrame (locked by DecoderLock)
{$IFDEF OldAVPacketAPI}
fAudioPaket: TAVPacket;
{$ELSE}
fAudioPaket: PAVPacket;
{$ENDIF}
{$IFNDEF UseFrameDecoderAPI}
fAudioPaketData: PByteArray;
{$ENDIF}
fAudioPaketSize: integer;
fAudioPaketSilence: integer; // number of bytes of silence to return

Expand Down Expand Up @@ -282,7 +291,12 @@ procedure TFFmpegDecodeStream.Reset();
fLoop := false;
fQuitRequest := false;

{$IFNDEF UseFrameDecoderAPI}
fAudioPaketData := nil;
{$ENDIF}
{$IFNDEF OldAVPacketAPI}
fAudioPaket := nil;
{$ENDIF}
fAudioPaketSize := 0;
fAudioPaketSilence := 0;

Expand All @@ -293,11 +307,8 @@ procedure TFFmpegDecodeStream.Reset();
fParserPauseRequestCount := 0;
fDecoderLocked := false;
fDecoderPauseRequestCount := 0;

{$IFDEF OldAVPacketAPI}
FillChar(fAudioPaket, SizeOf(TAVPacket), 0);
{$IF (LIBAVFORMAT_VERSION >= 59000000)}
// avoid calling av_packet_unref before fetching first frame
fAudioPaket.data := Pointer(STATUS_PACKET);
{$ENDIF}
end;

Expand Down Expand Up @@ -334,7 +345,6 @@ function TFFmpegDecodeStream.Open(const Filename: IPath): boolean;
var
SampleFormat: TAudioSampleFormat;
PackedSampleFormat: TAVSampleFormat;
TestFrame: TAVPacket;
AVResult: integer;
CodecID: TAVCodecID;
NumChannels: cint;
Expand Down Expand Up @@ -372,11 +382,6 @@ function TFFmpegDecodeStream.Open(const Filename: IPath): boolean;
// generate PTS values if they do not exist
fFormatCtx^.flags := fFormatCtx^.flags or AVFMT_FLAG_GENPTS;

// retrieve stream information
//{$IF LIBAVFORMAT_VERSION >= 54006000)}
// av_find_stream_info is deprecated and should be replaced by av_read_frame. Untested.
//AVResult := av_read_frame(fFormatCtx, TestFrame);

{$IF LIBAVFORMAT_VERSION >= 53002000)}
AVResult := avformat_find_stream_info(fFormatCtx, nil);
{$ELSE}
Expand Down Expand Up @@ -625,8 +630,13 @@ procedure TFFmpegDecodeStream.Close();
if (fAudioPaket.data <> nil) then
av_free_packet(@fAudioPaket);
{$ELSE}
{$IFDEF OldAVPacketAPI}
if (PAnsiChar(fAudioPaket.data) <> STATUS_PACKET) then
av_packet_unref(@fAudioPaket);
{$ELSE}
if (fAudioPaket <> nil) then
av_packet_free(@fAudioPaket);
{$ENDIF}
{$ENDIF}

PerformOnClose();
Expand Down Expand Up @@ -847,7 +857,11 @@ procedure TFFmpegDecodeStream.Parse();
*)
function TFFmpegDecodeStream.ParseLoop(): boolean;
var
{$IFDEF OldAVPacketAPI}
Packet: TAVPacket;
{$ELSE}
Packet: PAVPacket;
{$ENDIF}
SeekTarget: int64;
{$IF FFMPEG_VERSION_INT < 1001000}
ByteIOCtx: PByteIOContext;
Expand All @@ -859,6 +873,7 @@ function TFFmpegDecodeStream.ParseLoop(): boolean;
StartSilencePtr: PDouble; // pointer for the EMPTY status packet
fileSize: integer;
urlError: integer;
errnum: cint;

// Note: pthreads wakes threads waiting on a mutex in the order of their
// priority and not in FIFO order. SDL does not provide any option to
Expand All @@ -883,6 +898,9 @@ function TFFmpegDecodeStream.ParseLoop(): boolean;

begin
Result := true;
{$IFNDEF OldAVPacketAPI}
Packet := nil;
{$ENDIF}

while LockParser() do
begin
Expand Down Expand Up @@ -972,7 +990,14 @@ function TFFmpegDecodeStream.ParseLoop(): boolean;
Continue;
end;

if (av_read_frame(fFormatCtx, Packet) < 0) then
{$IFNDEF OldAVPacketAPI}
if (Packet = nil) then
Packet := av_packet_alloc();
errnum := av_read_frame(fFormatCtx, Packet);
{$ELSE}
errnum := av_read_frame(fFormatCtx, @Packet);
{$ENDIF}
if (errnum < 0) then
begin
// failed to read a frame, check reason
{$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)}
Expand All @@ -985,7 +1010,7 @@ function TFFmpegDecodeStream.ParseLoop(): boolean;
{$IF (LIBAVFORMAT_VERSION_MAJOR < 56)}
if (url_feof(ByteIOCtx) <> 0) then
{$ELSE}
if (avio_feof(ByteIOCtx) <> 0) then
if (errnum = AVERROR_EOF) then
{$IFEND}
begin
SDL_LockMutex(fStateLock);
Expand Down Expand Up @@ -1036,13 +1061,25 @@ function TFFmpegDecodeStream.ParseLoop(): boolean;
Break;
end;

{$IFDEF OldAVPacketAPI}
if (Packet.stream_index = fAudioStreamIndex) then
fPacketQueue.Put(@Packet)
{$ELSE}
if (Packet^.stream_index = fAudioStreamIndex) then
begin;
fPacketQueue.Put(Packet);
Packet := nil;
end
{$ENDIF}
else
{$IF (LIBAVFORMAT_VERSION < 59000000)}
av_free_packet(@Packet);
{$ELSE}
{$IFDEF OldAVPacketAPI}
av_packet_unref(@Packet);
{$ELSE}
av_packet_unref(Packet);
{$ENDIF}
{$ENDIF}

finally
Expand Down Expand Up @@ -1221,7 +1258,9 @@ function TFFmpegDecodeStream.DecodeFrame(): integer;
Break;
end;

{$IFNDEF UseFrameDecoderAPI}
Inc(PByte(fAudioPaketData), PaketDecodedSize);
{$ENDIF}
Dec(fAudioPaketSize, PaketDecodedSize);

// check if avcodec_decode_audio returned data, otherwise fetch more frames
Expand All @@ -1241,8 +1280,13 @@ function TFFmpegDecodeStream.DecodeFrame(): integer;
if (fAudioPaket.data <> nil) then
av_free_packet(@fAudioPaket);
{$ELSE}
{$IFDEF OldAVPacketAPI}
if (PAnsiChar(fAudioPaket.data) <> STATUS_PACKET) then
av_packet_unref(@fAudioPaket);
{$ELSE}
if ((fAudioPaket <> nil) and (PAnsiChar(fAudioPaket^.data) <> STATUS_PACKET)) then
av_packet_free(@fAudioPaket);
{$ENDIF}
{$ENDIF}

// do not block queue on seeking (to avoid deadlocks on the DecoderLock)
Expand All @@ -1257,15 +1301,25 @@ function TFFmpegDecodeStream.DecodeFrame(): integer;
Exit;

// handle Status-packet
{$IFDEF OldAVPacketAPI}
if (PAnsiChar(fAudioPaket.data) = STATUS_PACKET) then
{$ELSE}
if (PAnsiChar(fAudioPaket^.data) = STATUS_PACKET) then
{$ENDIF}
begin
{$IF (LIBAVFORMAT_VERSION < 59000000)}
fAudioPaket.data := nil;
{$ENDIF}
{$IFNDEF UseFrameDecoderAPI}
fAudioPaketData := nil;
{$ENDIF}
fAudioPaketSize := 0;

{$IFDEF OldAVPacketAPI}
case (fAudioPaket.flags) of
{$ELSE}
case (fAudioPaket^.flags) of
{$ENDIF}
PKT_STATUS_FLAG_FLUSH:
begin
// just used if SetPositionIntern was called without the flush flag.
Expand All @@ -1287,39 +1341,70 @@ function TFFmpegDecodeStream.DecodeFrame(): integer;
end;
PKT_STATUS_FLAG_EMPTY:
begin
{$IFDEF OldAVPacketAPI}
SilenceDuration := PDouble(fPacketQueue.GetStatusInfo(fAudioPaket))^;
fAudioPaketSilence := Round(SilenceDuration * fFormatInfo.SampleRate) * fFormatInfo.FrameSize;
fPacketQueue.FreeStatusInfo(fAudioPaket);
{$ELSE}
SilenceDuration := PDouble(fPacketQueue.GetStatusInfo(fAudioPaket^))^;
fAudioPaketSilence := Round(SilenceDuration * fFormatInfo.SampleRate) * fFormatInfo.FrameSize;
fPacketQueue.FreeStatusInfo(fAudioPaket^);
{$ENDIF}
end
else
begin
Log.LogStatus('Unknown status', 'TFFmpegDecodeStream.DecodeFrame');
end;
end;

{$IFNDEF OldAVPacketAPI}
av_packet_free(@fAudioPaket);
{$ENDIF}
Continue;
end;

{$IFDEF OldAVPacketAPI}
{$IFNDEF UseFrameDecoderAPI}
fAudioPaketData := fAudioPaket.data;
{$ENDIF}
fAudioPaketSize := fAudioPaket.size;
{$ELSE}
fAudioPaketSize := fAudioPaket^.size;
{$ENDIF}

{$IF LIBAVCODEC_VERSION >= 57037100}
{$IFDEF OldAVPacketAPI}
avcodec_send_packet(fCodecCtx, @fAudioPaket);
{$ELSE}
avcodec_send_packet(fCodecCtx, fAudioPaket);
{$IFEND}
{$IFEND}

// if available, update the stream position to the presentation time of this package
{$IFDEF OldAVPacketAPI}
if(fAudioPaket.pts <> AV_NOPTS_VALUE) then
{$ELSE}
if(fAudioPaket^.pts <> AV_NOPTS_VALUE) then
{$ENDIF}
begin
{$IFDEF DebugFFmpegDecode}
TmpPos := fAudioStreamPos;
{$ENDIF}
{$IFDEF OldAVPacketAPI}
fAudioStreamPos := av_q2d(fAudioStream^.time_base) * fAudioPaket.pts;
{$ELSE}
fAudioStreamPos := av_q2d(fAudioStream^.time_base) * fAudioPaket^.pts;
{$ENDIF}
{$IFDEF DebugFFmpegDecode}
DebugWriteln('Timestamp: ' + floattostrf(fAudioStreamPos, ffFixed, 15, 3) + ' ' +
'(Calc: ' + floattostrf(TmpPos, ffFixed, 15, 3) + '), ' +
'Diff: ' + floattostrf(fAudioStreamPos-TmpPos, ffFixed, 15, 3));
{$ENDIF}
end;

{$IFNDEF OldAVPacketAPI}
av_packet_free(@fAudioPaket);
{$ENDIF}
end;
end;

Expand Down
Loading

0 comments on commit 1035863

Please sign in to comment.