ffmpeg: using avformat instead of codec parsers.

This commit is contained in:
Grigory Chudov
2018-04-01 22:39:46 -04:00
parent c17d02cb61
commit 1dff3e434d
2 changed files with 104 additions and 114 deletions

View File

@@ -52,7 +52,7 @@ namespace CUETools.Codecs.ffmpegdll
var ffmpegDirectory = System.IO.Path.Combine(current, probe); var ffmpegDirectory = System.IO.Path.Combine(current, probe);
if (Directory.Exists(ffmpegDirectory)) if (Directory.Exists(ffmpegDirectory))
{ {
Console.WriteLine($"FFmpeg binaries found in: {ffmpegDirectory}"); System.Diagnostics.Trace.WriteLine($"FFmpeg binaries found in: {ffmpegDirectory}");
RegisterLibrariesSearchPath(ffmpegDirectory); RegisterLibrariesSearchPath(ffmpegDirectory);
break; break;
} }
@@ -74,7 +74,7 @@ namespace CUETools.Codecs.ffmpegdll
if (decoded_frame == null) if (decoded_frame == null)
throw new Exception("Could not allocate audio frame"); throw new Exception("Could not allocate audio frame");
ffmpeg.avcodec_register_all(); //ffmpeg.avcodec_register_all();
ffmpeg.av_register_all(); ffmpeg.av_register_all();
#if DEBUG #if DEBUG
@@ -95,117 +95,101 @@ namespace CUETools.Codecs.ffmpegdll
ffmpeg.av_log_set_callback(logCallback); ffmpeg.av_log_set_callback(logCallback);
#endif #endif
if (m_stream.CanSeek) m_read_packet_callback = readPacketCallback;
m_seek_callback = seekCallback;
int ret;
AVFormatContext* new_fmt_ctx = ffmpeg.avformat_alloc_context();
if (new_fmt_ctx == null)
throw new Exception("ffmpeg.avformat_alloc_context() failed");
ulong avio_ctx_buffer_size = 65536;
void* avio_ctx_buffer = ffmpeg.av_malloc(avio_ctx_buffer_size);
AVIOContext* avio_ctx = ffmpeg.avio_alloc_context((byte*)avio_ctx_buffer, (int)avio_ctx_buffer_size,
0, null, m_read_packet_callback, null, m_seek_callback);
if (avio_ctx == null)
{ {
m_read_packet_callback = readPacketCallback; ffmpeg.avformat_free_context(new_fmt_ctx);
m_seek_callback = seekCallback; throw new Exception("Cannot find stream information");
}
int ret; new_fmt_ctx->pb = avio_ctx;
AVFormatContext* fmt_ctx = ffmpeg.avformat_alloc_context();
if (fmt_ctx == null)
throw new Exception("ffmpeg.avformat_alloc_context() failed");
ulong avio_ctx_buffer_size = 4096; AVInputFormat* fmt = ffmpeg.av_find_input_format(m_settings.Format);
void* avio_ctx_buffer = ffmpeg.av_malloc(avio_ctx_buffer_size); if (fmt == null)
{
ffmpeg.avformat_free_context(new_fmt_ctx);
throw new Exception($"Cannot find input format ${m_settings.Format}");
}
AVIOContext* avio_ctx = ffmpeg.avio_alloc_context((byte*)avio_ctx_buffer, (int)avio_ctx_buffer_size, if ((ret = ffmpeg.avformat_open_input(&new_fmt_ctx, null, fmt, null)) < 0)
0, null, m_read_packet_callback, null, m_seek_callback); {
if (avio_ctx == null) ffmpeg.avformat_free_context(new_fmt_ctx);
{ throw new Exception("Cannot open input file");
ffmpeg.avformat_free_context(fmt_ctx); }
throw new Exception("Cannot find stream information");
}
fmt_ctx->pb = avio_ctx; if ((ret = ffmpeg.avformat_find_stream_info(new_fmt_ctx, null)) < 0)
{
AVInputFormat* fmt = ffmpeg.av_find_input_format(m_settings.Format); ffmpeg.avformat_close_input(&new_fmt_ctx);
if (fmt==null) throw new Exception("Cannot find stream information");
{ }
ffmpeg.avformat_free_context(fmt_ctx);
throw new Exception($"Cannot find input format ${m_settings.Format}");
}
if ((ret = ffmpeg.avformat_open_input(&fmt_ctx, null, fmt, null)) < 0)
{
ffmpeg.avformat_close_input(&fmt_ctx);
throw new Exception("Cannot open input file");
}
if ((ret = ffmpeg.avformat_find_stream_info(fmt_ctx, null)) < 0)
{
ffmpeg.avformat_close_input(&fmt_ctx);
throw new Exception("Cannot find stream information");
}
#if FINDBESTSTREAM #if FINDBESTSTREAM
/* select the audio stream */ /* select the audio stream */
ret = ffmpeg.av_find_best_stream(fmt_ctx, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0); ret = ffmpeg.av_find_best_stream(new_fmt_ctx, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);
if (ret < 0) if (ret < 0)
{ {
ffmpeg.avformat_close_input(&fmt_ctx); ffmpeg.avformat_close_input(&new_fmt_ctx);
throw new Exception("Cannot find an audio stream in the input file"); throw new Exception("Cannot find an audio stream in the input file");
} }
#else #else
if (fmt_ctx->nb_streams != 1) if (new_fmt_ctx->nb_streams != 1)
{ {
ffmpeg.avformat_close_input(&fmt_ctx); ffmpeg.avformat_close_input(&new_fmt_ctx);
throw new Exception("More than one stream"); throw new Exception("More than one stream");
} }
#endif #endif
int audio_stream_index = 0; // ret int audio_stream_index = 0; // ret
if (fmt_ctx->streams[audio_stream_index]->duration > 0) if (new_fmt_ctx->streams[audio_stream_index]->duration > 0)
_sampleCount = fmt_ctx->streams[audio_stream_index]->duration; _sampleCount = new_fmt_ctx->streams[audio_stream_index]->duration;
else else
_sampleCount = -1; _sampleCount = -1;
pcm = new AudioPCMConfig(
fmt_ctx->streams[audio_stream_index]->codecpar->bits_per_raw_sample,
fmt_ctx->streams[audio_stream_index]->codecpar->channels,
fmt_ctx->streams[audio_stream_index]->codecpar->sample_rate,
(AudioPCMConfig.SpeakerConfig)0);// fmt_ctx->streams[audio_stream_index]->codecpar->channel_layout);
// ret = ffmpeg.av_read_frame(fmt_ctx, pkt); int bps = new_fmt_ctx->streams[audio_stream_index]->codecpar->bits_per_raw_sample != 0 ?
new_fmt_ctx->streams[audio_stream_index]->codecpar->bits_per_raw_sample :
new_fmt_ctx->streams[audio_stream_index]->codecpar->bits_per_coded_sample;
int channels = new_fmt_ctx->streams[audio_stream_index]->codecpar->channels;
int sample_rate = new_fmt_ctx->streams[audio_stream_index]->codecpar->sample_rate;
ulong channel_layout = new_fmt_ctx->streams[audio_stream_index]->codecpar->channel_layout;
pcm = new AudioPCMConfig(bps, channels, sample_rate, (AudioPCMConfig.SpeakerConfig)0);
ffmpeg.avformat_close_input(&fmt_ctx); // ret = ffmpeg.av_read_frame(new_fmt_ctx, pkt);
fmt_ctx = null;
m_stream.Seek(0, SeekOrigin.Begin); fmt_ctx = new_fmt_ctx;
}
//m_stream.Seek(0, SeekOrigin.Begin);
codec = ffmpeg.avcodec_find_decoder(m_settings.Codec); codec = ffmpeg.avcodec_find_decoder(m_settings.Codec);
if (codec == null) if (codec == null)
throw new Exception("Codec not found"); throw new Exception("Codec not found");
parser = ffmpeg.av_parser_init((int)codec->id);
if (parser == null)
throw new Exception("Parser not found\n");
c = ffmpeg.avcodec_alloc_context3(codec); c = ffmpeg.avcodec_alloc_context3(codec);
if (c == null) if (c == null)
throw new Exception("Could not allocate audio codec context"); throw new Exception("Could not allocate audio codec context");
// ffmpeg.av_opt_set_int(c, "refcounted_frames", 1, 0); // ffmpeg.av_opt_set_int(c, "refcounted_frames", 1, 0);
// ffmpeg.avcodec_parameters_to_context(c, fmt_ctx->streams[audio_stream_index]->codecpar); ffmpeg.avcodec_parameters_to_context(c, fmt_ctx->streams[audio_stream_index]->codecpar);
c->request_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S32;
/* open it */ /* open it */
if (ffmpeg.avcodec_open2(c, codec, null) < 0) if (ffmpeg.avcodec_open2(c, codec, null) < 0)
throw new Exception("Could not open codec"); throw new Exception("Could not open codec");
data_buf = new byte[AUDIO_INBUF_SIZE];
data_size = 0;
data_offs = 0;
m_decoded_frame_offset = 0; m_decoded_frame_offset = 0;
m_decoded_frame_size = 0; m_decoded_frame_size = 0;
if (pcm == null)
{
fill();
_sampleCount = -1;
pcm = new AudioPCMConfig(
c->bits_per_raw_sample, c->channels, c->sample_rate,
(AudioPCMConfig.SpeakerConfig)0); // c->channel_layout;
}
_sampleOffset = 0; _sampleOffset = 0;
} }
@@ -231,14 +215,15 @@ namespace CUETools.Codecs.ffmpegdll
} }
} }
AVFormatContext* fmt_ctx1 = fmt_ctx;
ffmpeg.avformat_close_input(&fmt_ctx1);
fmt_ctx = null;
AVCodecContext* c1 = c; AVCodecContext* c1 = c;
ffmpeg.avcodec_free_context(&c1); ffmpeg.avcodec_free_context(&c1);
c = c1; c = c1;
//c = null; //c = null;
ffmpeg.av_parser_close(parser);
parser = null;
AVFrame* decoded_frame1 = decoded_frame; AVFrame* decoded_frame1 = decoded_frame;
ffmpeg.av_frame_free(&decoded_frame1); ffmpeg.av_frame_free(&decoded_frame1);
decoded_frame = decoded_frame1; decoded_frame = decoded_frame1;
@@ -291,6 +276,9 @@ namespace CUETools.Codecs.ffmpegdll
byte[] _readBuffer; byte[] _readBuffer;
int readPacketCallback(void* @opaque, byte* @buf, int @buf_size) int readPacketCallback(void* @opaque, byte* @buf, int @buf_size)
{ {
// TODO: if instead of calling ffmpeg.av_malloc for
// the buffer we pass to ffmpeg.avio_alloc_context
// we just pin _readBuffer, we wouldn't need to Copy.
if (_readBuffer == null || _readBuffer.Length < @buf_size) if (_readBuffer == null || _readBuffer.Length < @buf_size)
_readBuffer = new byte[Math.Max(@buf_size, 0x4000)]; _readBuffer = new byte[Math.Max(@buf_size, 0x4000)];
int len = m_stream.Read(_readBuffer, 0, @buf_size); int len = m_stream.Read(_readBuffer, 0, @buf_size);
@@ -306,9 +294,6 @@ namespace CUETools.Codecs.ffmpegdll
return m_stream.Seek(@offset, (SeekOrigin)@whence); return m_stream.Seek(@offset, (SeekOrigin)@whence);
} }
const int AUDIO_INBUF_SIZE = 65536;
const int AUDIO_REFILL_THRESH = 4096;
private void fill() private void fill()
{ {
while (true) while (true)
@@ -325,31 +310,21 @@ namespace CUETools.Codecs.ffmpegdll
m_decoded_frame_size = decoded_frame->nb_samples; m_decoded_frame_size = decoded_frame->nb_samples;
return; return;
} }
ret = ffmpeg.av_read_frame(fmt_ctx, pkt);
if (ret != 0)
{
if (ret == ffmpeg.AVERROR_EOF)
return;
byte* buf = stackalloc byte[256];
ffmpeg.av_strerror(ret, buf, 256);
throw new Exception("Error while parsing: " + Marshal.PtrToStringAnsi((IntPtr)buf));
}
if (pkt->size != 0) if (pkt->size != 0)
{ {
/* send the packet with the compressed data to the decoder */ /* send the packet with the compressed data to the decoder */
ret = ffmpeg.avcodec_send_packet(c, pkt); ret = ffmpeg.avcodec_send_packet(c, pkt);
if (ret < 0) throw new Exception("Error submitting the packet to the decoder"); if (ret < 0) throw new Exception("Error submitting the packet to the decoder");
pkt->size = 0;
continue;
} }
if (data_size < AUDIO_REFILL_THRESH)
{
Array.Copy(data_buf, data_offs, data_buf, 0, data_size);
data_offs = 0;
int len = m_stream.Read(data_buf, data_size, data_buf.Length - data_size);
data_size += len;
}
// int ret = ffmpeg.av_read_frame(fmt_ctx, pkt);
fixed (byte* data = &data_buf[data_offs])
ret = ffmpeg.av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, ffmpeg.AV_NOPTS_VALUE, ffmpeg.AV_NOPTS_VALUE, 0);
if (ret < 0 && ret != ffmpeg.AVERROR(ffmpeg.EAGAIN))
throw new Exception("Error while parsing");
if (pkt->size == 0 && data_size == 0 && ret == 0)
return;
data_offs += ret;
data_size -= ret;
} }
} }
@@ -371,6 +346,9 @@ namespace CUETools.Codecs.ffmpegdll
} }
long copyCount = Math.Min(samplesNeeded, m_decoded_frame_size); long copyCount = Math.Min(samplesNeeded, m_decoded_frame_size);
// TODO: if AudioBuffer supported different sample formats,
// this would be simpler. One complication though we would still
// need shifts.
switch (c->sample_fmt) switch (c->sample_fmt)
{ {
case AVSampleFormat.AV_SAMPLE_FMT_S32: case AVSampleFormat.AV_SAMPLE_FMT_S32:
@@ -387,6 +365,23 @@ namespace CUETools.Codecs.ffmpegdll
} }
} }
break; break;
case AVSampleFormat.AV_SAMPLE_FMT_S16P:
for (Int32 iChan = 0; iChan < _channelCount; iChan++)
{
fixed (int* pMyBuffer = &buff.Samples[buffOffset, iChan])
{
int* pMyBufferPtr = pMyBuffer;
short* pFLACBuffer = (short*)(decoded_frame->data[(uint)iChan]) + m_decoded_frame_offset;
short* pFLACBufferEnd = pFLACBuffer + copyCount;
while (pFLACBuffer < pFLACBufferEnd)
{
*pMyBufferPtr = *pFLACBuffer;
pMyBufferPtr += _channelCount;
pFLACBuffer++;
}
}
}
break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
@@ -405,8 +400,8 @@ namespace CUETools.Codecs.ffmpegdll
AVPacket* pkt; AVPacket* pkt;
AVFrame* decoded_frame; AVFrame* decoded_frame;
AVCodec* codec; AVCodec* codec;
AVCodecParserContext* parser;
AVCodecContext* c; AVCodecContext* c;
AVFormatContext* fmt_ctx;
avio_alloc_context_read_packet m_read_packet_callback; avio_alloc_context_read_packet m_read_packet_callback;
avio_alloc_context_seek m_seek_callback; avio_alloc_context_seek m_seek_callback;
@@ -417,9 +412,5 @@ namespace CUETools.Codecs.ffmpegdll
Stream m_stream; Stream m_stream;
long m_decoded_frame_offset; long m_decoded_frame_offset;
long m_decoded_frame_size; long m_decoded_frame_size;
byte[] data_buf;
int data_size;
int data_offs;
} }
} }

View File

@@ -99,7 +99,6 @@ namespace CUETools.Codecs.ffmpegdll
this.Init(); this.Init();
} }
} }
// This one doesn't work; There's a decoder but no parser (demuxer)
public class APEDecoderSettings : DecoderSettings, IAudioDecoderSettings public class APEDecoderSettings : DecoderSettings, IAudioDecoderSettings
{ {
public override string Extension => "ape"; public override string Extension => "ape";