1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2018-2019 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <nel/sound/audio_decoder_ffmpeg.h>
22 #define __STDC_CONSTANT_MACROS
25 #include <libavcodec/avcodec.h>
26 #include <libavformat/avformat.h>
27 #include <libswresample/swresample.h>
28 #include <libavutil/opt.h>
32 using namespace NLMISC
;
33 using namespace NLSOUND
;
35 // Visual Studio does not support AV_TIME_BASE_Q macro in C++
37 static const AVRational AV_TIME_BASE_Q
= {1, AV_TIME_BASE
};
41 const std::string
av_err2string(sint err
)
43 char buf
[AV_ERROR_MAX_STRING_SIZE
];
44 av_strerror(err
, buf
, AV_ERROR_MAX_STRING_SIZE
);
45 return (std::string
)buf
;
48 void nel_logger(void *ptr
, int level
, const char *fmt
, va_list vargs
)
50 static char msg
[1024];
52 const char *module
= NULL
;
54 // AV_LOG_DEBUG, AV_LOG_TRACE
55 if (level
>= AV_LOG_DEBUG
) return;
59 AVClass
*avc
= *(AVClass
**) ptr
;
60 module
= avc
->item_name(ptr
);
63 vsnprintf(msg
, sizeof(msg
), fmt
, vargs
);
64 msg
[sizeof(msg
)-1] = '\0';
69 // ffmpeg is about to crash so lets throw
70 nlerror("FFMPEG(P): (%s) %s", module
, msg
);
73 // ffmpeg had unrecoverable error, corrupted stream or such
74 nlerrornoex("FFMPEG(F): (%s) %s", module
, msg
);
77 nlwarning("FFMPEG(E): (%s) %s", module
, msg
);
80 nlwarning("FFMPEG(W): (%s) %s", module
, msg
);
83 nlinfo("FFMPEG(I): (%s) %s", module
, msg
);
86 nldebug("FFMPEG(V): (%s) %s", module
, msg
);
89 nldebug("FFMPEG(D): (%s) %s", module
, msg
);
92 nlinfo("FFMPEG: invalid log level:%d (%s) %s", level
, module
, msg
);
102 av_log_set_level(AV_LOG_DEBUG
);
103 av_log_set_callback(nel_logger
);
107 //avformat_network_init();
110 virtual ~CFfmpegInstance()
112 //avformat_network_deinit();
116 CFfmpegInstance ffmpeg
;
118 // Send bytes to ffmpeg
119 int avio_read_packet(void *opaque
, uint8
*buf
, int buf_size
)
121 NLSOUND::CAudioDecoderFfmpeg
*decoder
= static_cast<NLSOUND::CAudioDecoderFfmpeg
*>(opaque
);
122 NLMISC::IStream
*stream
= decoder
->getStream();
123 nlassert(stream
->isReading());
125 uint32 available
= decoder
->getStreamSize() - stream
->getPos();
126 if (available
== 0) return 0;
128 buf_size
= FFMIN(buf_size
, available
);
129 stream
->serialBuffer((uint8
*)buf
, buf_size
);
133 sint64
avio_seek(void *opaque
, sint64 offset
, int whence
)
135 NLSOUND::CAudioDecoderFfmpeg
*decoder
= static_cast<NLSOUND::CAudioDecoderFfmpeg
*>(opaque
);
136 NLMISC::IStream
*stream
= decoder
->getStream();
137 nlassert(stream
->isReading());
139 NLMISC::IStream::TSeekOrigin origin
;
143 origin
= NLMISC::IStream::begin
;
146 origin
= NLMISC::IStream::current
;
149 origin
= NLMISC::IStream::end
;
152 return decoder
->getStreamSize();
157 stream
->seek((sint32
) offset
, origin
);
158 return stream
->getPos();
165 // swresample will convert audio to this format
166 #define FFMPEG_SAMPLE_RATE 44100
167 #define FFMPEG_CHANNELS 2
168 #define FFMPEG_CHANNEL_LAYOUT AV_CH_LAYOUT_STEREO
169 #define FFMPEG_BITS_PER_SAMPLE 16
170 #define FFMPEG_SAMPLE_FORMAT AV_SAMPLE_FMT_S16
172 CAudioDecoderFfmpeg::CAudioDecoderFfmpeg(NLMISC::IStream
*stream
, bool loop
)
174 _Stream(stream
), _Loop(loop
), _IsMusicEnded(false), _StreamSize(0), _IsSupported(false),
175 _AvioContext(NULL
), _FormatContext(NULL
),
176 _AudioContext(NULL
), _AudioStreamIndex(0),
179 _StreamOffset
= stream
->getPos();
180 stream
->seek(0, NLMISC::IStream::end
);
181 _StreamSize
= stream
->getPos();
182 stream
->seek(_StreamOffset
, NLMISC::IStream::begin
);
185 _FormatContext
= avformat_alloc_context();
187 throw Exception("Can't create AVFormatContext");
189 // avio_ctx_buffer can be reallocated by ffmpeg and assigned to avio_ctx->buffer
190 uint8
*avio_ctx_buffer
= NULL
;
191 size_t avio_ctx_buffer_size
= 4096;
192 avio_ctx_buffer
= static_cast<uint8
*>(av_malloc(avio_ctx_buffer_size
));
193 if (!avio_ctx_buffer
)
194 throw Exception("Can't allocate avio context buffer");
196 _AvioContext
= avio_alloc_context(avio_ctx_buffer
, avio_ctx_buffer_size
, 0, this, &avio_read_packet
, NULL
, &avio_seek
);
198 throw Exception("Can't allocate avio context");
200 _FormatContext
->pb
= _AvioContext
;
201 sint ret
= avformat_open_input(&_FormatContext
, NULL
, NULL
, NULL
);
203 throw Exception("avformat_open_input: %d", ret
);
205 // find stream and then audio codec to see if ffmpeg supports this
206 _IsSupported
= false;
207 if (avformat_find_stream_info(_FormatContext
, NULL
) >= 0)
209 _AudioStreamIndex
= av_find_best_stream(_FormatContext
, AVMEDIA_TYPE_AUDIO
, -1, -1, NULL
, 0);
210 if (_AudioStreamIndex
>= 0)
212 _AudioContext
= _FormatContext
->streams
[_AudioStreamIndex
]->codec
;
213 av_opt_set_int(_AudioContext
, "refcounted_frames", 1, 0);
215 AVCodec
*codec
= avcodec_find_decoder(_AudioContext
->codec_id
);
216 if (codec
!= NULL
&& avcodec_open2(_AudioContext
, codec
, NULL
) >= 0)
232 nlwarning("FFMPEG: Decoder created, unknown stream format / codec");
236 CAudioDecoderFfmpeg::~CAudioDecoderFfmpeg()
241 void CAudioDecoderFfmpeg::release()
244 swr_free(&_SwrContext
);
247 avcodec_close(_AudioContext
);
250 avformat_close_input(&_FormatContext
);
252 if (_AvioContext
&& _AvioContext
->buffer
)
253 av_freep(&_AvioContext
->buffer
);
256 av_freep(&_AvioContext
);
259 bool CAudioDecoderFfmpeg::isFormatSupported() const
264 /// Get information on a music file.
265 bool CAudioDecoderFfmpeg::getInfo(NLMISC::IStream
*stream
, std::string
&artist
, std::string
&title
, float &length
)
267 CAudioDecoderFfmpeg
ffmpeg(stream
, false);
268 if (!ffmpeg
.isFormatSupported())
277 AVDictionaryEntry
*tag
= NULL
;
278 while((tag
= av_dict_get(ffmpeg
._FormatContext
->metadata
, "", tag
, AV_DICT_IGNORE_SUFFIX
)))
280 if (!strcmp(tag
->key
, "artist"))
284 else if (!strcmp(tag
->key
, "title"))
290 length
= ffmpeg
.getLength();
295 uint32
CAudioDecoderFfmpeg::getRequiredBytes()
297 return 0; // no minimum requirement of bytes to buffer out
300 uint32
CAudioDecoderFfmpeg::getNextBytes(uint8
*buffer
, uint32 minimum
, uint32 maximum
)
302 if (_IsMusicEnded
) return 0;
303 nlassert(minimum
<= maximum
); // can't have this..
305 // TODO: CStreamFileSource::play() will stall when there is no frames on warmup
306 // supported can be set false if there is an issue creating converter
309 _IsMusicEnded
= true;
313 uint32 bytes_read
= 0;
316 AVPacket packet
= {0};
320 sint64 in_channel_layout
= av_get_default_channel_layout(_AudioContext
->channels
);
321 _SwrContext
= swr_alloc_set_opts(NULL
,
323 FFMPEG_CHANNEL_LAYOUT
, FFMPEG_SAMPLE_FORMAT
, FFMPEG_SAMPLE_RATE
,
325 in_channel_layout
, _AudioContext
->sample_fmt
, _AudioContext
->sample_rate
,
327 swr_init(_SwrContext
);
331 while(bytes_read
< minimum
)
333 // read packet from stream
334 if ((ret
= av_read_frame(_FormatContext
, &packet
)) < 0)
336 _IsMusicEnded
= true;
341 if (packet
.stream_index
== _AudioStreamIndex
)
343 // packet can contain multiple frames
344 AVPacket first
= packet
;
348 ret
= avcodec_decode_audio4(_AudioContext
, &frame
, &got_frame
, &packet
);
351 nlwarning("FFMPEG: error decoding audio frame: %s", av_err2string(ret
).c_str());
359 uint32 out_bps
= av_get_bytes_per_sample(FFMPEG_SAMPLE_FORMAT
) * FFMPEG_CHANNELS
;
360 uint32 max_samples
= (maximum
- bytes_read
) / out_bps
;
362 uint32 out_samples
= av_rescale_rnd(swr_get_delay(_SwrContext
, _AudioContext
->sample_rate
) + frame
.nb_samples
,
363 FFMPEG_SAMPLE_RATE
, _AudioContext
->sample_rate
, AV_ROUND_UP
);
365 if (max_samples
> out_samples
)
366 max_samples
= out_samples
;
368 uint32 converted
= swr_convert(_SwrContext
, &buffer
, max_samples
, (const uint8
**)frame
.extended_data
, frame
.nb_samples
);
369 uint32 size
= out_bps
* converted
;
374 av_frame_unref(&frame
);
376 } while (got_frame
&& packet
.size
> 0);
378 av_packet_unref(&first
);
383 av_packet_unref(&packet
);
390 uint8
CAudioDecoderFfmpeg::getChannels()
392 return FFMPEG_CHANNELS
;
395 uint
CAudioDecoderFfmpeg::getSamplesPerSec()
397 return FFMPEG_SAMPLE_RATE
;
400 uint8
CAudioDecoderFfmpeg::getBitsPerSample()
402 return FFMPEG_BITS_PER_SAMPLE
;
405 bool CAudioDecoderFfmpeg::isMusicEnded()
407 return _IsMusicEnded
;
410 float CAudioDecoderFfmpeg::getLength()
413 if (_FormatContext
->duration
!= AV_NOPTS_VALUE
)
415 length
= _FormatContext
->duration
* av_q2d(AV_TIME_BASE_Q
);
417 else if (_FormatContext
->streams
[_AudioStreamIndex
]->duration
!= AV_NOPTS_VALUE
)
419 length
= _FormatContext
->streams
[_AudioStreamIndex
]->duration
* av_q2d(_FormatContext
->streams
[_AudioStreamIndex
]->time_base
);
424 void CAudioDecoderFfmpeg::setLooping(bool loop
)
429 } /* namespace NLSOUND */