Skip to content

Commit

Permalink
Merge pull request #235 from s09bQ5/audio-fixes
Browse files Browse the repository at this point in the history
Audio decoding with recent FFmpeg version
  • Loading branch information
basisbit committed Feb 19, 2017
2 parents 8db1d6a + 9df394e commit 0aaba49
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 25 deletions.
2 changes: 1 addition & 1 deletion configure
Expand Up @@ -6230,7 +6230,7 @@ $as_echo_n "checking version of ffmpeg... " >&6; }
else
as_fn_error $? "
Unsupported ffmpeg version, most recent version supported is 2.8.
Unsupported ffmpeg version, most recent version supported is 3.2.
" "$LINENO" 5
fi
Expand Down
2 changes: 1 addition & 1 deletion dists/autogen/m4/pkg_config_utils.m4
Expand Up @@ -143,7 +143,7 @@ AC_DEFUN([PKG_VERSION],
else
AC_MSG_ERROR([
Unsupported ffmpeg version, most recent version supported is 2.8.
Unsupported ffmpeg version, most recent version supported is 3.2.
])
fi
AX_EXTRACT_VERSION(FFMPEG, $FFMPEG_VERSION)
Expand Down
5 changes: 5 additions & 0 deletions src/lib/ffmpeg-3.1/avutil.pas
Expand Up @@ -358,4 +358,9 @@ function av_mallocz_array(nmemb: size_t; size: size_t): pointer; {$IFDEF HasInli
av_mallocz_array := av_mallocz(nmemb * size);
end;

function AVERROR(e: integer): integer;
begin
AVERROR := AVERROR_SIGN * e;
end;

end.
3 changes: 3 additions & 0 deletions src/lib/ffmpeg-3.1/libavutil/error.pas
Expand Up @@ -46,9 +46,11 @@
ENOSYS = ESysENOSYS;
EILSEQ = ESysEILSEQ;
EPIPE = ESysEPIPE;
EAGAIN = ESysEAGAIN;
{$ELSE}
ENOENT = 2;
EIO = 5;
EAGAIN = 11;
ENOMEM = 12;
EINVAL = 22;
EPIPE = 32; // just an assumption. needs to be checked.
Expand Down Expand Up @@ -86,6 +88,7 @@
#define AVUNERROR(e) (e)
#endif
*)
function AVERROR(e: integer): integer;

const

Expand Down
5 changes: 5 additions & 0 deletions src/lib/ffmpeg-3.2/avutil.pas
Expand Up @@ -358,4 +358,9 @@ function av_mallocz_array(nmemb: size_t; size: size_t): pointer; {$IFDEF HasInli
av_mallocz_array := av_mallocz(nmemb * size);
end;

function AVERROR(e: integer): integer;
begin
AVERROR := AVERROR_SIGN * e;
end;

end.
3 changes: 3 additions & 0 deletions src/lib/ffmpeg-3.2/libavutil/error.pas
Expand Up @@ -46,9 +46,11 @@
ENOSYS = ESysENOSYS;
EILSEQ = ESysEILSEQ;
EPIPE = ESysEPIPE;
EAGAIN = ESysEAGAIN;
{$ELSE}
ENOENT = 2;
EIO = 5;
EAGAIN = 11;
ENOMEM = 12;
EINVAL = 22;
EPIPE = 32; // just an assumption. needs to be checked.
Expand Down Expand Up @@ -86,6 +88,7 @@
#define AVUNERROR(e) (e)
#endif
*)
function AVERROR(e: integer): integer;

const

Expand Down
83 changes: 60 additions & 23 deletions src/media/UAudioDecoder_FFmpeg.pas
Expand Up @@ -116,6 +116,9 @@ TFFmpegDecodeStream = class(TAudioDecodeStream)
fPacketQueue: TPacketQueue;

fFormatInfo: TAudioFormatInfo;
{$IF LIBAVCODEC_VERSION >= 57000000}
fBytesPerSample: integer;
{$IFEND}

// FFmpeg specific data
fFormatCtx: PAVFormatContext;
Expand All @@ -142,6 +145,9 @@ TFFmpegDecodeStream = class(TAudioDecodeStream)
fAudioBufferPos: integer;
fAudioBufferSize: integer;
fAudioBuffer: PByteArray;
{$IF LIBAVCODEC_VERSION >= 57000000}
fAudioBufferFrame: PAVFrame;
{$IFEND}

fFilename: IPath;

Expand All @@ -158,7 +164,7 @@ TFFmpegDecodeStream = class(TAudioDecodeStream)
procedure PauseParser();
procedure ResumeParser();

function DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer;
function DecodeFrame(): integer;
procedure FlushCodecBuffers();
procedure PauseDecoder();
procedure ResumeDecoder();
Expand Down Expand Up @@ -211,6 +217,9 @@ constructor TFFmpegDecodeStream.Create();
fDecoderUnlockedCond := SDL_CreateCond();
fDecoderResumeCond := SDL_CreateCond();

{$IF LIBAVCODEC_VERSION >= 57000000}
fAudioBufferFrame := av_frame_alloc();
{$ELSE}
// according to the documentation of avcodec_decode_audio(2), sample-data
// should be aligned on a 16 byte boundary. Otherwise internal calls
// (e.g. to SSE or Altivec operations) might fail or lack performance on some
Expand All @@ -227,6 +236,7 @@ constructor TFFmpegDecodeStream.Create();
// was not applicable as Delphi in contrast to FPC provides at most 8 byte
// alignment ({$ALIGN 16} is not supported) by this directive.
fAudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16);
{$IFEND}

Reset();
end;
Expand Down Expand Up @@ -271,7 +281,11 @@ destructor TFFmpegDecodeStream.Destroy();
SDL_DestroyCond(fDecoderUnlockedCond);
SDL_DestroyCond(fDecoderResumeCond);

{$IF LIBAVCODEC_VERSION >= 57000000}
av_frame_free(@fAudioBufferFrame);
{$ELSE}
FreeAlignedMem(fAudioBuffer);
{$IFEND}

inherited;
end;
Expand Down Expand Up @@ -426,6 +440,9 @@ function TFFmpegDecodeStream.Open(const Filename: IPath): boolean;
fCodecCtx^.sample_rate,
SampleFormat
);
{$IF LIBAVCODEC_VERSION >= 57000000}
fBytesPerSample := av_get_bytes_per_sample(fCodecCtx^.sample_fmt) * fCodecCtx^.channels;
{$IFEND}

fPacketQueue := TPacketQueue.Create();

Expand Down Expand Up @@ -976,36 +993,39 @@ procedure TFFmpegDecodeStream.FlushCodecBuffers();
end;
end;

function TFFmpegDecodeStream.DecodeFrame(Buffer: PByteArray; BufferSize: integer): integer;
function TFFmpegDecodeStream.DecodeFrame(): integer;
var
PaketDecodedSize: integer; // size of packet data used for decoding
DataSize: integer; // size of output data decoded by FFmpeg
BlockQueue: boolean;
SilenceDuration: double;
{$IF (LIBAVCODEC_VERSION >= 52122000) and (LIBAVCODEC_VERSION < 57037100)}
AVPacket: TAVPacket;
{$IFEND}
{$IF LIBAVCODEC_VERSION >= 57000000}
AVFrame: PAVFrame;
got_frame_ptr: integer;
{$IFEND}
{$IFDEF DebugFFmpegDecode}
TmpPos: double;
{$ENDIF}
begin
Result := -1;
{$IF LIBAVCODEC_VERSION >= 57000000}
got_frame_ptr := 1;
{$IFEND}

if (EOF) then
Exit;

{$IF (LIBAVCODEC_VERSION >= 52122000) and (LIBAVCODEC_VERSION < 57037100)}
AVPacket := fAudioPaket;
{$IFEND}

while(true) do
begin
// for titles with start_time > 0 we have to generate silence
// until we reach the pts of the first data packet.
if (fAudioPaketSilence > 0) then
begin
DataSize := Min(fAudioPaketSilence, BufferSize);
FillChar(Buffer[0], DataSize, 0);
DataSize := Min(fAudioPaketSilence, AUDIO_BUFFER_SIZE);
FillChar(fAudioBuffer[0], DataSize, 0);
Dec(fAudioPaketSilence, DataSize);
fAudioStreamPos := fAudioStreamPos + DataSize / fFormatInfo.BytesPerSec;
Result := DataSize;
Expand All @@ -1015,30 +1035,43 @@ function TFFmpegDecodeStream.DecodeFrame(Buffer: PByteArray; BufferSize: integer
// read packet data
while (fAudioPaketSize > 0) do
begin
DataSize := BufferSize;
DataSize := AUDIO_BUFFER_SIZE;
{$IF (LIBAVCODEC_VERSION >= 52122000) and (LIBAVCODEC_VERSION < 57037100)}
AVPacket.data := fAudioPaketData;
AVPacket.size := fAudioPaketSize;
{$IFEND}

{$IF LIBAVCODEC_VERSION >= 57000000}
AVFrame := av_frame_alloc();
PaketDecodedSize := avcodec_decode_audio4(fCodecCtx, AVFrame,
@got_frame_ptr, @fAudioPaket);
DataSize := AVFrame.nb_samples;
Buffer := PByteArray(AVFrame.data[0]);
{$IF LIBAVCODEC_VERSION >= 57037100}
got_frame_ptr := avcodec_receive_frame(fCodecCtx, fAudioBufferFrame);
if (got_frame_ptr = AVERROR(EAGAIN)) then
PaketDecodedSize := fAudioPaketSize
else
PaketDecodedSize := 0;
got_frame_ptr := ord(got_frame_ptr = 0);
{$ELSE}
PaketDecodedSize := avcodec_decode_audio4(fCodecCtx, fAudioBufferFrame,
@got_frame_ptr, @AVPacket);
{$IFEND}
if(got_frame_ptr <> 0) then
begin
DataSize := fAudioBufferFrame.nb_samples * fBytesPerSample;
fAudioBuffer := PByteArray(fAudioBufferFrame.data[0]);
end
else
DataSize := 0;
{$ELSEIF LIBAVCODEC_VERSION >= 52122000} // 52.122.0
PaketDecodedSize := avcodec_decode_audio3(fCodecCtx, PSmallint(Buffer),
DataSize, @fAudioPaket);
PaketDecodedSize := avcodec_decode_audio3(fCodecCtx, PSmallint(fAudioBuffer),
DataSize, @AVPacket);
{$ELSEIF LIBAVCODEC_VERSION >= 51030000} // 51.30.0
PaketDecodedSize := avcodec_decode_audio2(fCodecCtx, PSmallint(Buffer),
PaketDecodedSize := avcodec_decode_audio2(fCodecCtx, PSmallint(fAudioBuffer),
DataSize, fAudioPaketData, fAudioPaketSize);
{$ELSE}
PaketDecodedSize := avcodec_decode_audio(fCodecCtx, PSmallint(Buffer),
PaketDecodedSize := avcodec_decode_audio(fCodecCtx, PSmallint(fAudioBuffer),
DataSize, fAudioPaketData, fAudioPaketSize);
{$IFEND}

{$IF LIBAVCODEC_VERSION >= 57000000}
if(PaketDecodedSize < 0) or (got_frame_ptr < 1) then
{$ELSE}
if(PaketDecodedSize < 0) then
{$IFEND}
begin
// if error, skip frame
{$IFDEF DebugFFmpegDecode}
Expand Down Expand Up @@ -1123,6 +1156,10 @@ function TFFmpegDecodeStream.DecodeFrame(Buffer: PByteArray; BufferSize: integer
fAudioPaketData := fAudioPaket.data;
fAudioPaketSize := fAudioPaket.size;

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

// if available, update the stream position to the presentation time of this package
if(fAudioPaket.pts <> AV_NOPTS_VALUE) then
begin
Expand Down Expand Up @@ -1184,7 +1221,7 @@ function TFFmpegDecodeStream.ReadData(Buffer: PByteArray; BufferSize: integer):
fAudioBufferPos := 0;

// we have already sent all our data; get more
fAudioBufferSize := DecodeFrame(fAudioBuffer, AUDIO_BUFFER_SIZE);
fAudioBufferSize := DecodeFrame();

// check for errors or EOF
if(fAudioBufferSize < 0) then
Expand Down

1 comment on commit 0aaba49

@bohning
Copy link
Collaborator

@bohning bohning commented on 0aaba49 Feb 19, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works on Mac with homebrew ffmpeg 3.2.2 (latest available is 3.2.4, but this one is not accepted by configure: Unsupported ffmpeg version, most recent version supported is 3.2.).

Thank you so much, @s09bQ5! I will test this on the Raspberry as soon as I get a chance.

Please sign in to comment.