*/
-
-#include "config.h"
+#include "aubio_priv.h"
#ifdef HAVE_LIBAV
#include <libavutil/opt.h>
#include <stdlib.h>
+// determine whether we use libavformat from ffmpeg or from libav
+#define FFMPEG_LIBAVFORMAT (LIBAVFORMAT_VERSION_MICRO > 99 )
+// max_analyze_duration2 was used from ffmpeg libavformat 55.43.100 through 57.2.100
+#define FFMPEG_LIBAVFORMAT_MAX_DUR2 FFMPEG_LIBAVFORMAT && ( \
+ (LIBAVFORMAT_VERSION_MAJOR == 55 && LIBAVFORMAT_VERSION_MINOR >= 43) \
+ || (LIBAVFORMAT_VERSION_MAJOR == 56) \
+ || (LIBAVFORMAT_VERSION_MAJOR == 57 && LIBAVFORMAT_VERSION_MINOR < 2) \
+ )
+
+// backward compatibility with libavcodec55
+#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
+#warning "libavcodec55 is deprecated"
+#define HAVE_AUBIO_LIBAVCODEC_DEPRECATED 1
+#define av_frame_alloc avcodec_alloc_frame
+#define av_frame_free avcodec_free_frame
+#define av_packet_unref av_free_packet
+#endif
+
#include "aubio_priv.h"
#include "fvec.h"
#include "fmat.h"
AVFormatContext *avFormatCtx;
AVCodecContext *avCodecCtx;
AVFrame *avFrame;
+ AVPacket avPacket;
AVAudioResampleContext *avr;
- float *output;
+ smpl_t *output;
uint_t read_samples;
uint_t read_index;
sint_t selected_stream;
void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s, uint_t multi);
void aubio_source_avcodec_readframe(aubio_source_avcodec_t *s, uint_t * read_samples);
-aubio_source_avcodec_t * new_aubio_source_avcodec(char_t * path, uint_t samplerate, uint_t hop_size) {
+uint_t aubio_source_avcodec_has_network_url(aubio_source_avcodec_t *s);
+
+uint_t aubio_source_avcodec_has_network_url(aubio_source_avcodec_t *s) {
+ char proto[20], authorization[256], hostname[128], uripath[256];
+ int proto_size = 20, authorization_size = 256, hostname_size = 128,
+ *port_ptr = 0, path_size = 256;
+ av_url_split(proto, proto_size, authorization, authorization_size, hostname,
+ hostname_size, port_ptr, uripath, path_size, s->path);
+ if (strlen(proto)) {
+ return 1;
+ }
+ return 0;
+}
+
+
+aubio_source_avcodec_t * new_aubio_source_avcodec(const char_t * path, uint_t samplerate, uint_t hop_size) {
aubio_source_avcodec_t * s = AUBIO_NEW(aubio_source_avcodec_t);
int err;
if (path == NULL) {
- AUBIO_ERR("Aborted opening null path\n");
+ AUBIO_ERR("source_avcodec: Aborted opening null path\n");
goto beach;
}
if ((sint_t)samplerate < 0) {
- AUBIO_ERR("Can not open %s with samplerate %d\n", path, samplerate);
+ AUBIO_ERR("source_avcodec: Can not open %s with samplerate %d\n", path, samplerate);
goto beach;
}
if ((sint_t)hop_size <= 0) {
- AUBIO_ERR("Can not open %s with hop_size %d\n", path, hop_size);
+ AUBIO_ERR("source_avcodec: Can not open %s with hop_size %d\n", path, hop_size);
goto beach;
}
s->hop_size = hop_size;
s->channels = 1;
- s->path = path;
+
+ if (s->path) AUBIO_FREE(s->path);
+ s->path = AUBIO_ARRAY(char_t, strnlen(path, PATH_MAX) + 1);
+ strncpy(s->path, path, strnlen(path, PATH_MAX) + 1);
// register all formats and codecs
av_register_all();
- // if path[0] != '/'
- //avformat_network_init();
+ if (aubio_source_avcodec_has_network_url(s)) {
+ avformat_network_init();
+ }
// try opening the file and get some info about it
AVFormatContext *avFormatCtx = s->avFormatCtx;
if ( (err = avformat_open_input(&avFormatCtx, s->path, NULL, NULL) ) < 0 ) {
char errorstr[256];
av_strerror (err, errorstr, sizeof(errorstr));
- AUBIO_ERR("Failed opening %s (%s)\n", s->path, errorstr);
+ AUBIO_ERR("source_avcodec: Failed opening %s (%s)\n", s->path, errorstr);
goto beach;
}
// try to make sure max_analyze_duration is big enough for most songs
+#if FFMPEG_LIBAVFORMAT_MAX_DUR2
+ avFormatCtx->max_analyze_duration2 *= 100;
+#else
avFormatCtx->max_analyze_duration *= 100;
+#endif
// retrieve stream information
if ( (err = avformat_find_stream_info(avFormatCtx, NULL)) < 0 ) {
char errorstr[256];
av_strerror (err, errorstr, sizeof(errorstr));
- AUBIO_ERR("Could not find stream information " "for %s (%s)\n", s->path,
+ AUBIO_ERR("source_avcodec: Could not find stream information " "for %s (%s)\n", s->path,
errorstr);
goto beach;
}
uint_t i;
sint_t selected_stream = -1;
for (i = 0; i < avFormatCtx->nb_streams; i++) {
+#if FF_API_LAVF_AVCTX
+ if (avFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+#else
if (avFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
+#endif
if (selected_stream == -1) {
selected_stream = i;
} else {
- AUBIO_WRN("More than one audio stream in %s, "
+ AUBIO_WRN("source_avcodec: More than one audio stream in %s, "
"taking the first one\n", s->path);
}
}
}
if (selected_stream == -1) {
- AUBIO_ERR("No audio stream in %s\n", s->path);
+ AUBIO_ERR("source_avcodec: No audio stream in %s\n", s->path);
goto beach;
}
//AUBIO_DBG("Taking stream %d in file %s\n", selected_stream, s->path);
s->selected_stream = selected_stream;
AVCodecContext *avCodecCtx = s->avCodecCtx;
+#if FF_API_LAVF_AVCTX
+ AVCodecParameters *codecpar = avFormatCtx->streams[selected_stream]->codecpar;
+ if (codecpar == NULL) {
+ AUBIO_ERR("source_avcodec: Could not find decoder for %s", s->path);
+ goto beach;
+ }
+ AVCodec *codec = avcodec_find_decoder(codecpar->codec_id);
+
+ /* Allocate a codec context for the decoder */
+ avCodecCtx = avcodec_alloc_context3(codec);
+ if (!avCodecCtx) {
+ AUBIO_ERR("source_avcodec: Failed to allocate the %s codec context for path %s\n",
+ av_get_media_type_string(AVMEDIA_TYPE_AUDIO), s->path);
+ goto beach;
+ }
+#else
avCodecCtx = avFormatCtx->streams[selected_stream]->codec;
AVCodec *codec = avcodec_find_decoder(avCodecCtx->codec_id);
+#endif
if (codec == NULL) {
- AUBIO_ERR("Could not find decoder for %s", s->path);
+ AUBIO_ERR("source_avcodec: Could not find decoder for %s", s->path);
goto beach;
}
+#if FF_API_LAVF_AVCTX
+ /* Copy codec parameters from input stream to output codec context */
+ if ((err = avcodec_parameters_to_context(avCodecCtx, codecpar)) < 0) {
+ AUBIO_ERR("source_avcodec: Failed to copy %s codec parameters to decoder context for %s\n",
+ av_get_media_type_string(AVMEDIA_TYPE_AUDIO), s->path);
+ goto beach;
+ }
+#endif
+
if ( ( err = avcodec_open2(avCodecCtx, codec, NULL) ) < 0) {
char errorstr[256];
av_strerror (err, errorstr, sizeof(errorstr));
- AUBIO_ERR("Could not load codec for %s (%s)\n", s->path, errorstr);
+ AUBIO_ERR("source_avcodec: Could not load codec for %s (%s)\n", s->path, errorstr);
goto beach;
}
//AUBIO_DBG("input_channels: %d\n", s->input_channels);
if (samplerate == 0) {
- samplerate = s->input_samplerate;
- //AUBIO_DBG("sampling rate set to 0, automagically adjusting to %d\n", samplerate);
+ s->samplerate = s->input_samplerate;
+ } else {
+ s->samplerate = samplerate;
}
- s->samplerate = samplerate;
if (s->samplerate > s->input_samplerate) {
- AUBIO_WRN("upsampling %s from %d to %d\n", s->path,
+ AUBIO_WRN("source_avcodec: upsampling %s from %d to %d\n", s->path,
s->input_samplerate, s->samplerate);
}
AVFrame *avFrame = s->avFrame;
- avFrame = avcodec_alloc_frame();
+ avFrame = av_frame_alloc();
if (!avFrame) {
- AUBIO_ERR("Could not allocate frame for (%s)\n", s->path);
+ AUBIO_ERR("source_avcodec: Could not allocate frame for (%s)\n", s->path);
}
/* allocate output for avr */
- s->output = (float *)av_malloc(AUBIO_AVCODEC_MAX_BUFFER_SIZE * sizeof(float));
+ s->output = (smpl_t *)av_malloc(AUBIO_AVCODEC_MAX_BUFFER_SIZE * sizeof(smpl_t));
s->read_samples = 0;
s->read_index = 0;
}
void aubio_source_avcodec_reset_resampler(aubio_source_avcodec_t * s, uint_t multi) {
+ // create or reset resampler to/from mono/multi-channel
if ( (multi != s->multi) || (s->avr == NULL) ) {
int64_t input_layout = av_get_default_channel_layout(s->input_channels);
uint_t output_channels = multi ? s->input_channels : 1;
int64_t output_layout = av_get_default_channel_layout(output_channels);
- if (s->avr != NULL) {
- avresample_close( s->avr );
- av_free ( s->avr );
- s->avr = NULL;
- }
- AVAudioResampleContext *avr = s->avr;
- avr = avresample_alloc_context();
+ AVAudioResampleContext *avr = avresample_alloc_context();
+ AVAudioResampleContext *oldavr = s->avr;
av_opt_set_int(avr, "in_channel_layout", input_layout, 0);
av_opt_set_int(avr, "out_channel_layout", output_layout, 0);
av_opt_set_int(avr, "in_sample_rate", s->input_samplerate, 0);
av_opt_set_int(avr, "out_sample_rate", s->samplerate, 0);
av_opt_set_int(avr, "in_sample_fmt", s->avCodecCtx->sample_fmt, 0);
+#if HAVE_AUBIO_DOUBLE
+ av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_DBL, 0);
+#else
av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
+#endif
+ // TODO: use planar?
+ //av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
int err;
if ( ( err = avresample_open(avr) ) < 0) {
char errorstr[256];
av_strerror (err, errorstr, sizeof(errorstr));
- AUBIO_ERR("Could not open AVAudioResampleContext for %s (%s)\n",
+ AUBIO_ERR("source_avcodec: Could not open AVAudioResampleContext for %s (%s)\n",
s->path, errorstr);
//goto beach;
return;
}
s->avr = avr;
+ if (oldavr != NULL) {
+ avresample_close( oldavr );
+ av_free ( oldavr );
+ oldavr = NULL;
+ }
s->multi = multi;
}
}
AVFormatContext *avFormatCtx = s->avFormatCtx;
AVCodecContext *avCodecCtx = s->avCodecCtx;
AVFrame *avFrame = s->avFrame;
- AVPacket avPacket;
+ AVPacket avPacket = s->avPacket;
av_init_packet (&avPacket);
AVAudioResampleContext *avr = s->avr;
- float *output = s->output;
+ smpl_t *output = s->output;
*read_samples = 0;
do
if (err != 0) {
char errorstr[256];
av_strerror (err, errorstr, sizeof(errorstr));
- AUBIO_ERR("Could not read frame in %s (%s)\n", s->path, errorstr);
+ AUBIO_ERR("source_avcodec: could not read frame in %s (%s)\n", s->path, errorstr);
goto beach;
}
} while (avPacket.stream_index != s->selected_stream);
int got_frame = 0;
+#if FF_API_LAVF_AVCTX
+ int ret = avcodec_send_packet(avCodecCtx, &avPacket);
+ if (ret < 0 && ret != AVERROR_EOF) {
+ AUBIO_ERR("source_avcodec: error when sending packet for %s\n", s->path);
+ goto beach;
+ }
+ ret = avcodec_receive_frame(avCodecCtx, avFrame);
+ if (ret >= 0) {
+ got_frame = 1;
+ }
+ if (ret < 0) {
+ if (ret == AVERROR(EAGAIN)) {
+ AUBIO_WRN("source_avcodec: output is not available right now - user must try to send new input\n");
+ } else if (ret == AVERROR_EOF) {
+ AUBIO_WRN("source_avcodec: the decoder has been fully flushed, and there will be no more output frames\n");
+ } else {
+ AUBIO_ERR("source_avcodec: decoding errors on %s\n", s->path);
+ goto beach;
+ }
+ }
+#else
int len = avcodec_decode_audio4(avCodecCtx, avFrame, &got_frame, &avPacket);
if (len < 0) {
- AUBIO_ERR("Error while decoding %s\n", s->path);
+ AUBIO_ERR("source_avcodec: error while decoding %s\n", s->path);
goto beach;
}
+#endif
if (got_frame == 0) {
- //AUBIO_ERR("Could not get frame for (%s)\n", s->path);
+ AUBIO_WRN("source_avcodec: did not get a frame when reading %s\n", s->path);
goto beach;
}
(uint8_t **)&output, out_linesize, max_out_samples,
(uint8_t **)avFrame->data, in_linesize, in_samples);
if (out_samples <= 0) {
- //AUBIO_ERR("No sample found while converting frame (%s)\n", s->path);
+ AUBIO_WRN("source_avcodec: no sample found while converting frame (%s)\n", s->path);
goto beach;
}
s->avr = avr;
s->output = output;
- av_free_packet(&avPacket);
+ av_packet_unref(&avPacket);
}
void aubio_source_avcodec_do(aubio_source_avcodec_t * s, fvec_t * read_data, uint_t * read){
- if (s->multi == 1) aubio_source_avcodec_reset_resampler(s, 0);
uint_t i;
uint_t end = 0;
uint_t total_wrote = 0;
+ // switch from multi
+ if (s->multi == 1) aubio_source_avcodec_reset_resampler(s, 0);
while (total_wrote < s->hop_size) {
end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
for (i = 0; i < end; i++) {
}
}
if (total_wrote < s->hop_size) {
- for (i = end; i < s->hop_size; i++) {
+ for (i = total_wrote; i < s->hop_size; i++) {
read_data->data[i] = 0.;
}
}
}
void aubio_source_avcodec_do_multi(aubio_source_avcodec_t * s, fmat_t * read_data, uint_t * read){
- if (s->multi == 0) aubio_source_avcodec_reset_resampler(s, 1);
uint_t i,j;
uint_t end = 0;
uint_t total_wrote = 0;
+ // switch from mono
+ if (s->multi == 0) aubio_source_avcodec_reset_resampler(s, 1);
while (total_wrote < s->hop_size) {
end = MIN(s->read_samples - s->read_index, s->hop_size - total_wrote);
for (j = 0; j < read_data->height; j++) {
}
if (total_wrote < s->hop_size) {
for (j = 0; j < read_data->height; j++) {
- for (i = end; i < s->hop_size; i++) {
+ for (i = total_wrote; i < s->hop_size; i++) {
read_data->data[j][i] = 0.;
}
}
*read = total_wrote;
}
-uint_t aubio_source_avcodec_get_samplerate(aubio_source_avcodec_t * s) {
+uint_t aubio_source_avcodec_get_samplerate(const aubio_source_avcodec_t * s) {
return s->samplerate;
}
-uint_t aubio_source_avcodec_get_channels(aubio_source_avcodec_t * s) {
+uint_t aubio_source_avcodec_get_channels(const aubio_source_avcodec_t * s) {
return s->input_channels;
}
int64_t min_ts = MAX(resampled_pos - 2000, 0);
int64_t max_ts = MIN(resampled_pos + 2000, INT64_MAX);
int seek_flags = AVSEEK_FLAG_FRAME | AVSEEK_FLAG_ANY;
- int ret = avformat_seek_file(s->avFormatCtx, s->selected_stream,
+ int ret = AUBIO_FAIL;
+ if (s->avFormatCtx != NULL && s->avr != NULL) {
+ ret = AUBIO_OK;
+ } else {
+ AUBIO_ERR("source_avcodec: failed seeking in %s (file not opened?)", s->path);
+ return ret;
+ }
+ if ((sint_t)pos < 0) {
+ AUBIO_ERR("source_avcodec: could not seek %s at %d (seeking position"
+ " should be >= 0)\n", s->path, pos);
+ return AUBIO_FAIL;
+ }
+ ret = avformat_seek_file(s->avFormatCtx, s->selected_stream,
min_ts, resampled_pos, max_ts, seek_flags);
if (ret < 0) {
- AUBIO_ERR("Failed seeking to %d in file %s", pos, s->path);
+ AUBIO_ERR("source_avcodec: failed seeking to %d in file %s", pos, s->path);
}
// reset read status
s->eof = 0;
return ret;
}
-void del_aubio_source_avcodec(aubio_source_avcodec_t * s){
- if (!s) return;
- if (s->output != NULL) {
- av_free(s->output);
+uint_t aubio_source_avcodec_get_duration (aubio_source_avcodec_t * s) {
+ if (s && &(s->avFormatCtx) != NULL) {
+ int64_t duration = s->avFormatCtx->duration;
+ return s->samplerate * ((uint_t)duration / 1e6 );
}
+ return 0;
+}
+
+uint_t aubio_source_avcodec_close(aubio_source_avcodec_t * s) {
if (s->avr != NULL) {
avresample_close( s->avr );
av_free ( s->avr );
}
s->avr = NULL;
- if (s->avFrame != NULL) {
- avcodec_free_frame( &(s->avFrame) );
- }
- s->avFrame = NULL;
if (s->avCodecCtx != NULL) {
avcodec_close ( s->avCodecCtx );
}
s->avCodecCtx = NULL;
if (s->avFormatCtx != NULL) {
- avformat_close_input ( &(s->avFormatCtx) );
+ avformat_close_input(&s->avFormatCtx);
+#ifndef HAVE_AUBIO_LIBAVCODEC_DEPRECATED // avoid crash on old libavcodec54
+ avformat_free_context(s->avFormatCtx);
+#endif
+ s->avFormatCtx = NULL;
+ }
+ av_packet_unref(&s->avPacket);
+ return AUBIO_OK;
+}
+
+void del_aubio_source_avcodec(aubio_source_avcodec_t * s){
+ if (!s) return;
+ aubio_source_avcodec_close(s);
+ if (s->output != NULL) {
+ av_free(s->output);
+ }
+ s->output = NULL;
+ if (s->avFrame != NULL) {
+ av_frame_free( &(s->avFrame) );
}
s->avFrame = NULL;
- s->avFormatCtx = NULL;
+ if (s->path) {
+ AUBIO_FREE(s->path);
+ }
+ s->path = NULL;
AUBIO_FREE(s);
}