From Javier Taibo, "I have found that since version 1.1, FFMPEG changed the way audio streams are retrieved, from packed to planar format. SDL interprets packed audio, as is used in the osgmovie example. To make the audio work when the OSGffmpeg plug-in is compiled against recent FFMPEG versions, FFmpegDecoderAudio must check for planar formats and in these cases request the samples as packed. This way all works as before. It can be checked with osgmovie example application.
$ osgmovie --audio movie.avi.ffmpeg FFmpegImageStream::open audio failed, audio stream will be disabled: unknown audio format With the attached FFmpegDecoderAudio.cpp, audio sounds correctly. I am also attaching a modified version of FindFFmpeg.cmake that allows to set as FFMPEG_DIR the ffmpeg compiled in the source directory structure. It should not break anything as it only adds some additional search paths. " git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14654 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
@@ -13,6 +13,10 @@
|
||||
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
|
||||
#define av_frame_alloc avcodec_alloc_frame
|
||||
#define av_frame_free avcodec_free_frame
|
||||
#endif
|
||||
|
||||
#if LIBAVCODEC_VERSION_MAJOR < 56
|
||||
#define AV_CODEC_ID_NONE CODEC_ID_NONE
|
||||
@@ -22,65 +26,85 @@ namespace osgFFmpeg {
|
||||
|
||||
static int decode_audio(AVCodecContext *avctx, int16_t *samples,
|
||||
int *frame_size_ptr,
|
||||
const uint8_t *buf, int buf_size)
|
||||
const uint8_t *buf, int buf_size,
|
||||
SwrContext *swr_context,
|
||||
int out_sample_rate,
|
||||
int out_nb_channels,
|
||||
AVSampleFormat out_sample_format)
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 56
|
||||
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
|
||||
if (!frame) return AVERROR(ENOMEM);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32)
|
||||
|
||||
AVPacket avpkt;
|
||||
av_init_packet(&avpkt);
|
||||
avpkt.data = const_cast<uint8_t *>(buf);
|
||||
avpkt.size = buf_size;
|
||||
|
||||
int got_frame = 0;
|
||||
int result = avcodec_decode_audio4(avctx, frame, &got_frame, &avpkt);
|
||||
AVFrame *frame = av_frame_alloc();
|
||||
int ret, got_frame = 0;
|
||||
|
||||
if (result>=0 && got_frame)
|
||||
{
|
||||
if (!frame)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
ret = avcodec_decode_audio4(avctx, frame, &got_frame, &avpkt);
|
||||
|
||||
if (ret >= 0 && got_frame) {
|
||||
int ch, plane_size;
|
||||
int planar = av_sample_fmt_is_planar(avctx->sample_fmt);
|
||||
int data_size = av_samples_get_buffer_size(&plane_size, avctx->channels, frame->nb_samples, avctx->sample_fmt, 1);
|
||||
if (*frame_size_ptr < data_size)
|
||||
{
|
||||
|
||||
int out_samples;
|
||||
// if sample rate changes, number of samples is different
|
||||
if ( out_sample_rate != avctx->sample_rate ) {
|
||||
// out_samples = av_rescale_rnd(swr_get_delay(swr_context, avctx->sample_rate) +
|
||||
// frame->nb_samples, out_sample_rate, avctx->sample_rate, AV_ROUND_UP);
|
||||
out_samples = av_rescale_rnd(frame->nb_samples, out_sample_rate, avctx->sample_rate, AV_ROUND_UP);
|
||||
}
|
||||
else {
|
||||
out_samples = frame->nb_samples;
|
||||
}
|
||||
|
||||
int output_data_size = av_samples_get_buffer_size(&plane_size, out_nb_channels,
|
||||
out_samples,
|
||||
out_sample_format, 1);
|
||||
|
||||
if (*frame_size_ptr < output_data_size) {
|
||||
av_log(avctx, AV_LOG_ERROR, "output buffer size is too small for "
|
||||
"the current frame (%d < %d)\n", *frame_size_ptr, data_size);
|
||||
"the current frame (%d < %d)\n", *frame_size_ptr, output_data_size);
|
||||
av_frame_free(&frame);
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
memcpy(samples, frame->extended_data[0], plane_size);
|
||||
if (planar && avctx->channels > 1)
|
||||
{
|
||||
uint8_t *out = ((uint8_t *)samples) + plane_size;
|
||||
for (ch = 1; ch < avctx->channels; ch++)
|
||||
{
|
||||
memcpy(out, frame->extended_data[ch], plane_size);
|
||||
out += plane_size;
|
||||
|
||||
// if resampling is needed, call swr_convert
|
||||
if ( swr_context != NULL ) {
|
||||
|
||||
out_samples = swr_convert(swr_context, (uint8_t **)&samples, out_samples,
|
||||
(const uint8_t **)frame->extended_data, frame->nb_samples);
|
||||
|
||||
// recompute output_data_size following swr_convert result (number of samples actually converted)
|
||||
output_data_size = av_samples_get_buffer_size(&plane_size, out_nb_channels,
|
||||
out_samples,
|
||||
out_sample_format, 1);
|
||||
}
|
||||
else {
|
||||
|
||||
memcpy(samples, frame->extended_data[0], plane_size);
|
||||
|
||||
if (planar && avctx->channels > 1) {
|
||||
uint8_t *out = ((uint8_t *)samples) + plane_size;
|
||||
for (ch = 1; ch < avctx->channels; ch++) {
|
||||
memcpy(out, frame->extended_data[ch], plane_size);
|
||||
out += plane_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
*frame_size_ptr = data_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
*frame_size_ptr = output_data_size;
|
||||
|
||||
} else {
|
||||
*frame_size_ptr = 0;
|
||||
}
|
||||
|
||||
av_frame_free(&frame);
|
||||
return ret;
|
||||
|
||||
return result;
|
||||
|
||||
#elif LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32)
|
||||
|
||||
// following code segment copied from ffmpeg's avcodec_decode_audio2()
|
||||
// implementation to avoid warnings about deprecated function usage.
|
||||
AVPacket avpkt;
|
||||
av_init_packet(&avpkt);
|
||||
avpkt.data = const_cast<uint8_t *>(buf);
|
||||
avpkt.size = buf_size;
|
||||
|
||||
return avcodec_decode_audio3(avctx, samples, frame_size_ptr, &avpkt);
|
||||
#else
|
||||
// fallback for older versions of ffmpeg that don't have avcodec_decode_audio3.
|
||||
return avcodec_decode_audio2(avctx, samples, frame_size_ptr, buf, buf_size);
|
||||
@@ -100,9 +124,9 @@ FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clo
|
||||
m_audio_buf_index(0),
|
||||
m_end_of_stream(false),
|
||||
m_paused(true),
|
||||
m_exit(false)
|
||||
m_exit(false),
|
||||
m_swr_context(NULL)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +138,7 @@ FFmpegDecoderAudio::~FFmpegDecoderAudio()
|
||||
|
||||
|
||||
|
||||
void FFmpegDecoderAudio::open(AVStream * const stream)
|
||||
void FFmpegDecoderAudio::open(AVStream * const stream, FFmpegParameters* parameters)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -125,51 +149,60 @@ void FFmpegDecoderAudio::open(AVStream * const stream)
|
||||
m_stream = stream;
|
||||
m_context = stream->codec;
|
||||
|
||||
m_frequency = m_context->sample_rate;
|
||||
m_nb_channels = m_context->channels;
|
||||
m_in_sample_rate = m_context->sample_rate;
|
||||
m_in_nb_channels = m_context->channels;
|
||||
m_in_sample_format = m_context->sample_fmt;
|
||||
|
||||
OSG_INFO<<"FFmpegDecoderAudio::open(..), m_nb_channels="<<m_nb_channels<<", m_context->sample_fmt="<<m_context->sample_fmt<<std::endl;
|
||||
AVDictionaryEntry *opt_out_sample_rate = av_dict_get( *parameters->getOptions(), "out_sample_rate", NULL, 0 );
|
||||
if ( opt_out_sample_rate )
|
||||
m_out_sample_rate = atoi(opt_out_sample_rate->value);
|
||||
else
|
||||
m_out_sample_rate = m_in_sample_rate;
|
||||
|
||||
switch (m_context->sample_fmt)
|
||||
AVDictionaryEntry *opt_out_sample_format = av_dict_get( *parameters->getOptions(), "out_sample_format", NULL, 0 );
|
||||
if ( opt_out_sample_format )
|
||||
m_out_sample_format = (AVSampleFormat) atoi(opt_out_sample_format->value);
|
||||
else
|
||||
// always packed, planar formats are evil!
|
||||
m_out_sample_format = av_get_packed_sample_fmt( m_in_sample_format );
|
||||
|
||||
AVDictionaryEntry *opt_out_nb_channels = av_dict_get( *parameters->getOptions(), "out_nb_channels", NULL, 0 );
|
||||
if ( opt_out_nb_channels )
|
||||
m_out_nb_channels = atoi(opt_out_nb_channels->value);
|
||||
else
|
||||
m_out_nb_channels = m_in_nb_channels;
|
||||
|
||||
if ( m_in_sample_rate != m_out_sample_rate
|
||||
|| m_in_nb_channels != m_out_nb_channels
|
||||
|| m_in_sample_format != m_out_sample_format )
|
||||
{
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
throw std::runtime_error("invalid audio format AV_SAMPLE_FMT_NONE");
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_U8;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S16;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S32;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_F32;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBL");
|
||||
#if 0
|
||||
printf("### CONVERTING from sample format %s TO %s\n\t\tFROM %d TO %d channels\n\t\tFROM %d Hz to %d Hz\n",
|
||||
av_get_sample_fmt_name(m_in_sample_format),
|
||||
av_get_sample_fmt_name(m_out_sample_format),
|
||||
m_in_nb_channels,
|
||||
m_out_nb_channels,
|
||||
m_in_sample_rate,
|
||||
m_out_sample_rate);
|
||||
#endif
|
||||
m_swr_context = swr_alloc_set_opts(NULL,
|
||||
av_get_default_channel_layout(m_out_nb_channels),
|
||||
m_out_sample_format,
|
||||
m_out_sample_rate,
|
||||
av_get_default_channel_layout(m_in_nb_channels),
|
||||
m_in_sample_format,
|
||||
m_in_sample_rate,
|
||||
0, NULL );
|
||||
|
||||
case AV_SAMPLE_FMT_U8P:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_U8;
|
||||
m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt );
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S16;
|
||||
m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt );
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S32;
|
||||
m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt );
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
m_sample_format = osg::AudioStream::SAMPLE_FORMAT_F32;
|
||||
m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt );
|
||||
break;
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBLP");
|
||||
int err = swr_init(m_swr_context);
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unknown audio format");
|
||||
if ( err ) {
|
||||
char error_string[512];
|
||||
av_strerror(err, error_string, 512);
|
||||
OSG_WARN << "FFmpegDecoderAudio - WARNING: Error initializing resampling context : " << error_string << std::endl;
|
||||
swr_free(&m_swr_context);
|
||||
throw std::runtime_error("swr_init() failed");;
|
||||
}
|
||||
}
|
||||
|
||||
// Check stream sanity
|
||||
@@ -189,6 +222,10 @@ void FFmpegDecoderAudio::open(AVStream * const stream)
|
||||
// Open codec
|
||||
if (avcodec_open2(m_context, p_codec, NULL) < 0)
|
||||
throw std::runtime_error("avcodec_open() failed");
|
||||
|
||||
m_context->get_buffer = avcodec_default_get_buffer;
|
||||
m_context->release_buffer = avcodec_default_release_buffer;
|
||||
|
||||
}
|
||||
|
||||
catch (...)
|
||||
@@ -219,6 +256,7 @@ void FFmpegDecoderAudio::close(bool waitForThreadToExit)
|
||||
if (waitForThreadToExit)
|
||||
join();
|
||||
}
|
||||
swr_free(&m_swr_context);
|
||||
}
|
||||
|
||||
void FFmpegDecoderAudio::setVolume(float volume)
|
||||
@@ -300,7 +338,7 @@ void FFmpegDecoderAudio::fillBuffer(void * const buffer, size_t size)
|
||||
|
||||
m_audio_buf_index += fill_size;
|
||||
|
||||
adjustBufferEndTps(fill_size);
|
||||
adjustBufferEndPts(fill_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,38 +395,37 @@ void FFmpegDecoderAudio::decodeLoop()
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FFmpegDecoderAudio::adjustBufferEndTps(const size_t buffer_size)
|
||||
void FFmpegDecoderAudio::adjustBufferEndPts(const size_t buffer_size)
|
||||
{
|
||||
int sample_size = nbChannels() * frequency();
|
||||
int bytes_per_second = nbChannels() * frequency();
|
||||
|
||||
switch (sampleFormat())
|
||||
{
|
||||
case osg::AudioStream::SAMPLE_FORMAT_U8:
|
||||
sample_size *= 1;
|
||||
bytes_per_second *= 1;
|
||||
break;
|
||||
|
||||
case osg::AudioStream::SAMPLE_FORMAT_S16:
|
||||
sample_size *= 2;
|
||||
bytes_per_second *= 2;
|
||||
break;
|
||||
|
||||
case osg::AudioStream::SAMPLE_FORMAT_S24:
|
||||
sample_size *= 3;
|
||||
bytes_per_second *= 3;
|
||||
break;
|
||||
|
||||
case osg::AudioStream::SAMPLE_FORMAT_S32:
|
||||
sample_size *= 4;
|
||||
bytes_per_second *= 4;
|
||||
break;
|
||||
|
||||
case osg::AudioStream::SAMPLE_FORMAT_F32:
|
||||
sample_size *= 4;
|
||||
bytes_per_second *= 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unsupported audio sample format");
|
||||
}
|
||||
|
||||
m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(sample_size));
|
||||
m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(bytes_per_second));
|
||||
}
|
||||
|
||||
|
||||
@@ -403,7 +440,7 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size)
|
||||
{
|
||||
int data_size = size;
|
||||
|
||||
const int bytes_decoded = decode_audio(m_context, reinterpret_cast<int16_t*>(buffer), &data_size, m_packet_data, m_bytes_remaining);
|
||||
const int bytes_decoded = decode_audio(m_context, reinterpret_cast<int16_t*>(buffer), &data_size, m_packet_data, m_bytes_remaining, m_swr_context, m_out_sample_rate, m_out_nb_channels, m_out_sample_format);
|
||||
|
||||
if (bytes_decoded < 0)
|
||||
{
|
||||
@@ -464,5 +501,33 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
osg::AudioStream::SampleFormat FFmpegDecoderAudio::sampleFormat() const
|
||||
{
|
||||
switch (m_out_sample_format)
|
||||
{
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
throw std::runtime_error("invalid audio format AV_SAMPLE_FMT_NONE");
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return osg::AudioStream::SAMPLE_FORMAT_U8;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return osg::AudioStream::SAMPLE_FORMAT_S16;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return osg::AudioStream::SAMPLE_FORMAT_S32;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return osg::AudioStream::SAMPLE_FORMAT_F32;
|
||||
break;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBL");
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unknown audio format");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace osgFFmpeg
|
||||
|
||||
Reference in New Issue
Block a user