TEST(API, Test2SilenceRawFp) { short zeroes[1024]; std::fill(zeroes, zeroes + 1024, 0); ChromaprintContext *ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_TEST2); ASSERT_NE(nullptr, ctx); SCOPE_EXIT(chromaprint_free(ctx)); ASSERT_EQ(1, chromaprint_start(ctx, 44100, 1)); for (int i = 0; i < 130; i++) { ASSERT_EQ(1, chromaprint_feed(ctx, zeroes, 1024)); } uint32_t *fp; int length; ASSERT_EQ(1, chromaprint_finish(ctx)); ASSERT_EQ(1, chromaprint_get_raw_fingerprint(ctx, &fp, &length)); SCOPE_EXIT(chromaprint_dealloc(fp)); ASSERT_EQ(3, length); EXPECT_EQ(627964279, fp[0]); EXPECT_EQ(627964279, fp[1]); EXPECT_EQ(627964279, fp[2]); }
TEST(API, Test2SilenceFp) { short zeroes[1024]; std::fill(zeroes, zeroes + 1024, 0); ChromaprintContext *ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_TEST2); ASSERT_NE(nullptr, ctx); SCOPE_EXIT(chromaprint_free(ctx)); ASSERT_EQ(1, chromaprint_start(ctx, 44100, 1)); for (int i = 0; i < 130; i++) { ASSERT_EQ(1, chromaprint_feed(ctx, zeroes, 1024)); } char *fp; uint32_t fp_hash; ASSERT_EQ(1, chromaprint_finish(ctx)); ASSERT_EQ(1, chromaprint_get_fingerprint(ctx, &fp)); SCOPE_EXIT(chromaprint_dealloc(fp)); ASSERT_EQ(1, chromaprint_get_fingerprint_hash(ctx, &fp_hash)); ASSERT_EQ(18, strlen(fp)); EXPECT_EQ(std::string("AQAAA0mUaEkSRZEGAA"), std::string(fp)); ASSERT_EQ(627964279, fp_hash); }
static int write_header(AVFormatContext *s) { ChromaprintMuxContext *cpr = s->priv_data; AVStream *st; avpriv_lock_avformat(); cpr->ctx = chromaprint_new(cpr->algorithm); avpriv_unlock_avformat(); if (!cpr->ctx) { av_log(s, AV_LOG_ERROR, "Failed to create chromaprint context.\n"); return AVERROR(ENOMEM); } if (cpr->silence_threshold != -1) { #if CPR_VERSION_INT >= AV_VERSION_INT(0, 7, 0) if (!chromaprint_set_option(cpr->ctx, "silence_threshold", cpr->silence_threshold)) { av_log(s, AV_LOG_ERROR, "Failed to set silence threshold.\n"); goto fail; } #else av_log(s, AV_LOG_ERROR, "Setting the silence threshold requires Chromaprint " "version 0.7.0 or later.\n"); goto fail; #endif } if (s->nb_streams != 1) { av_log(s, AV_LOG_ERROR, "Only one stream is supported\n"); goto fail; } st = s->streams[0]; if (st->codecpar->channels > 2) { av_log(s, AV_LOG_ERROR, "Only up to 2 channels are supported\n"); goto fail; } if (st->codecpar->sample_rate < 1000) { av_log(s, AV_LOG_ERROR, "Sampling rate must be at least 1000\n"); goto fail; } if (!chromaprint_start(cpr->ctx, st->codecpar->sample_rate, st->codecpar->channels)) { av_log(s, AV_LOG_ERROR, "Failed to start chromaprint\n"); goto fail; } return 0; fail: cleanup(cpr); return AVERROR(EINVAL); }
static GstFlowReturn gst_chromaprint_transform_ip (GstBaseTransform * trans, GstBuffer * buf) { GstChromaprint *chromaprint = GST_CHROMAPRINT (trans); GstAudioFilter *filter = GST_AUDIO_FILTER (trans); GstMapInfo map_info; guint nsamples; gint rate, channels; rate = GST_AUDIO_INFO_RATE (&filter->info); channels = GST_AUDIO_INFO_CHANNELS (&filter->info); if (G_UNLIKELY (rate <= 0 || channels <= 0)) return GST_FLOW_NOT_NEGOTIATED; if (!chromaprint->record) return GST_FLOW_OK; if (!gst_buffer_map (buf, &map_info, GST_MAP_READ)) return GST_FLOW_ERROR; nsamples = map_info.size / (channels * 2); if (nsamples == 0) goto end; if (chromaprint->nsamples == 0) { chromaprint_start (chromaprint->context, rate, channels); } chromaprint->nsamples += nsamples; chromaprint->duration = chromaprint->nsamples / rate; chromaprint_feed (chromaprint->context, map_info.data, map_info.size / sizeof (guint16)); if (chromaprint->duration >= chromaprint->max_duration && !chromaprint->fingerprint) { gst_chromaprint_create_fingerprint (chromaprint); } end: gst_buffer_unmap (buf, &map_info); return GST_FLOW_OK; }
static sout_stream_id_t *Add( sout_stream_t *p_stream, es_format_t *p_fmt ) { sout_stream_sys_t *p_sys = p_stream->p_sys; sout_stream_id_t *id = NULL; if ( p_fmt->i_cat == AUDIO_ES && !p_sys->id ) { if( p_fmt->i_codec != VLC_CODEC_S16N || p_fmt->audio.i_channels > 2 ) { msg_Warn( p_stream, "bad input format: need s16l, 1 or 2 channels" ); goto error; } id = malloc( sizeof( sout_stream_id_t ) ); if ( !id ) goto error; id->i_channels = p_fmt->audio.i_channels; id->i_samplerate = p_fmt->audio.i_rate; id->i_samples = p_sys->i_duration * id->i_samplerate; if ( !chromaprint_start( p_sys->p_chromaprint_ctx, p_fmt->audio.i_rate, id->i_channels ) ) { msg_Err( p_stream, "Failed starting chromaprint on %uHz %uch samples", p_fmt->audio.i_rate, id->i_channels ); goto error; } else { p_sys->id = id; msg_Dbg( p_stream, "Starting chromaprint on %uHz %uch samples", p_fmt->audio.i_rate, id->i_channels ); } return id; } error: free( id ); return NULL; }
TEST(API, TestFp) { std::vector<short> data = LoadAudioFile("data/test_stereo_44100.raw"); ChromaprintContext *ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_TEST2); ASSERT_NE(nullptr, ctx); SCOPE_EXIT(chromaprint_free(ctx)); ASSERT_EQ(1, chromaprint_get_num_channels(ctx)); ASSERT_EQ(11025, chromaprint_get_sample_rate(ctx)); ASSERT_EQ(1, chromaprint_start(ctx, 44100, 1)); ASSERT_EQ(1, chromaprint_feed(ctx, data.data(), data.size())); char *fp; uint32_t fp_hash; ASSERT_EQ(1, chromaprint_finish(ctx)); ASSERT_EQ(1, chromaprint_get_fingerprint(ctx, &fp)); SCOPE_EXIT(chromaprint_dealloc(fp)); ASSERT_EQ(1, chromaprint_get_fingerprint_hash(ctx, &fp_hash)); EXPECT_EQ(std::string("AQAAC0kkZUqYREkUnFAXHk8uuMZl6EfO4zu-4ABKFGESWIIMEQE"), std::string(fp)); ASSERT_EQ(3732003127, fp_hash); }
QString Chromaprinter::CreateFingerprint() { Q_ASSERT(QThread::currentThread() != qApp->thread()); buffer_.open(QIODevice::WriteOnly); GMainContext* context = g_main_context_new(); g_main_context_push_thread_default(context); event_loop_ = g_main_loop_new(context, FALSE); pipeline_ = gst_pipeline_new("pipeline"); GstElement* src = CreateElement("filesrc", pipeline_); GstElement* decode = CreateElement("decodebin2", pipeline_); GstElement* convert = CreateElement("audioconvert", pipeline_); GstElement* resample = CreateElement("audioresample", pipeline_); GstElement* sink = CreateElement("appsink", pipeline_); if (!src || !decode || !convert || !resample || !sink) { return QString(); } convert_element_ = convert; // Connect the elements gst_element_link_many(src, decode, nullptr); gst_element_link_many(convert, resample, nullptr); // Chromaprint expects mono floats at a sample rate of 11025Hz. GstCaps* caps = gst_caps_new_simple( "audio/x-raw-int", "width", G_TYPE_INT, 16, "channels", G_TYPE_INT, kDecodeChannels, "rate", G_TYPE_INT, kDecodeRate, nullptr); gst_element_link_filtered(resample, sink, caps); gst_caps_unref(caps); GstAppSinkCallbacks callbacks; memset(&callbacks, 0, sizeof(callbacks)); callbacks.new_buffer = NewBufferCallback; gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, nullptr); g_object_set(G_OBJECT(sink), "sync", FALSE, nullptr); g_object_set(G_OBJECT(sink), "emit-signals", TRUE, nullptr); // Set the filename g_object_set(src, "location", filename_.toUtf8().constData(), nullptr); // Connect signals CHECKED_GCONNECT(decode, "new-decoded-pad", &NewPadCallback, this); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this); guint bus_callback_id = gst_bus_add_watch( gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this); QTime time; time.start(); // Start playing gst_element_set_state(pipeline_, GST_STATE_PLAYING); g_main_loop_run(event_loop_); g_main_loop_unref(event_loop_); g_main_context_unref(context); int decode_time = time.restart(); buffer_.close(); QByteArray data = buffer_.data(); ChromaprintContext* chromaprint = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT); chromaprint_start(chromaprint, kDecodeRate, kDecodeChannels); chromaprint_feed(chromaprint, reinterpret_cast<void*>(data.data()), data.size() / 2); chromaprint_finish(chromaprint); void* fprint = nullptr; int size = 0; int ret = chromaprint_get_raw_fingerprint(chromaprint, &fprint, &size); QByteArray fingerprint; if (ret == 1) { void* encoded = nullptr; int encoded_size = 0; chromaprint_encode_fingerprint(fprint, size, CHROMAPRINT_ALGORITHM_DEFAULT, &encoded, &encoded_size, 1); fingerprint.append(reinterpret_cast<char*>(encoded), encoded_size); chromaprint_dealloc(fprint); chromaprint_dealloc(encoded); } chromaprint_free(chromaprint); int codegen_time = time.elapsed(); qLog(Debug) << "Decode time:" << decode_time << "Codegen time:" << codegen_time; // Cleanup callbacks.new_buffer = nullptr; gst_app_sink_set_callbacks(reinterpret_cast<GstAppSink*>(sink), &callbacks, this, nullptr); gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), nullptr, nullptr); g_source_remove(bus_callback_id); gst_element_set_state(pipeline_, GST_STATE_NULL); gst_object_unref(pipeline_); return fingerprint; }
int decode_audio_file(ChromaprintContext *chromaprint_ctx, int16_t *buffer1, int16_t *buffer2, const char *file_name, int max_length, int *duration) { int i, ok = 0, remaining, length, consumed, buffer_size, codec_ctx_opened = 0; AVFormatContext *format_ctx = NULL; AVCodecContext *codec_ctx = NULL; AVCodec *codec = NULL; AVStream *stream = NULL; AVPacket packet, packet_temp; #ifdef HAVE_AV_AUDIO_CONVERT AVAudioConvert *convert_ctx = NULL; #endif int16_t *buffer; if (!strcmp(file_name, "-")) { file_name = "pipe:0"; } #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0) if (av_open_input_file(&format_ctx, file_name, NULL, 0, NULL) != 0) { #else if (avformat_open_input(&format_ctx, file_name, NULL, NULL) != 0) { #endif fprintf(stderr, "ERROR: couldn't open the file\n"); goto done; } if (av_find_stream_info(format_ctx) < 0) { fprintf(stderr, "ERROR: couldn't find stream information in the file\n"); goto done; } for (i = 0; i < format_ctx->nb_streams; i++) { codec_ctx = format_ctx->streams[i]->codec; if (codec_ctx && codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { stream = format_ctx->streams[i]; break; } } if (!stream) { fprintf(stderr, "ERROR: couldn't find any audio stream in the file\n"); goto done; } codec = avcodec_find_decoder(codec_ctx->codec_id); if (!codec) { fprintf(stderr, "ERROR: unknown codec\n"); goto done; } if (avcodec_open(codec_ctx, codec) < 0) { fprintf(stderr, "ERROR: couldn't open the codec\n"); goto done; } codec_ctx_opened = 1; if (codec_ctx->channels <= 0) { fprintf(stderr, "ERROR: no channels found in the audio stream\n"); goto done; } if (codec_ctx->sample_fmt != AV_SAMPLE_FMT_S16) { #ifdef HAVE_AV_AUDIO_CONVERT convert_ctx = av_audio_convert_alloc(AV_SAMPLE_FMT_S16, codec_ctx->channels, codec_ctx->sample_fmt, codec_ctx->channels, NULL, 0); if (!convert_ctx) { fprintf(stderr, "ERROR: couldn't create sample format converter\n"); goto done; } #else fprintf(stderr, "ERROR: unsupported sample format\n"); goto done; #endif } *duration = stream->time_base.num * stream->duration / stream->time_base.den; av_init_packet(&packet); av_init_packet(&packet_temp); remaining = max_length * codec_ctx->channels * codec_ctx->sample_rate; chromaprint_start(chromaprint_ctx, codec_ctx->sample_rate, codec_ctx->channels); while (1) { if (av_read_frame(format_ctx, &packet) < 0) { break; } packet_temp.data = packet.data; packet_temp.size = packet.size; while (packet_temp.size > 0) { buffer_size = BUFFER_SIZE; #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 23, 0) consumed = avcodec_decode_audio2(codec_ctx, buffer1, &buffer_size, packet_temp.data, packet_temp.size); #else consumed = avcodec_decode_audio3(codec_ctx, buffer1, &buffer_size, &packet_temp); #endif if (consumed < 0) { break; } packet_temp.data += consumed; packet_temp.size -= consumed; if (buffer_size <= 0) { if (buffer_size < 0) { fprintf(stderr, "WARNING: size returned from avcodec_decode_audioX is too small\n"); } continue; } if (buffer_size > BUFFER_SIZE) { fprintf(stderr, "WARNING: size returned from avcodec_decode_audioX is too large\n"); continue; } #ifdef HAVE_AV_AUDIO_CONVERT if (convert_ctx) { const void *ibuf[6] = { buffer1 }; void *obuf[6] = { buffer2 }; #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 8, 0) int istride[6] = { av_get_bits_per_sample_format(codec_ctx->sample_fmt) / 8 }; #else int istride[6] = { av_get_bytes_per_sample(codec_ctx->sample_fmt) }; #endif int ostride[6] = { 2 }; int len = buffer_size / istride[0]; if (av_audio_convert(convert_ctx, obuf, ostride, ibuf, istride, len) < 0) { break; } buffer = buffer2; buffer_size = len * ostride[0]; } else { buffer = buffer1; } #else buffer = buffer1; #endif length = MIN(remaining, buffer_size / 2); if (!chromaprint_feed(chromaprint_ctx, buffer, length)) { fprintf(stderr, "ERROR: fingerprint calculation failed\n"); goto done; } if (max_length) { remaining -= length; if (remaining <= 0) { goto finish; } } } if (packet.data) { av_free_packet(&packet); } } finish: if (!chromaprint_finish(chromaprint_ctx)) { fprintf(stderr, "ERROR: fingerprint calculation failed\n"); goto done; } ok = 1; done: if (codec_ctx_opened) { avcodec_close(codec_ctx); } if (format_ctx) { av_close_input_file(format_ctx); } #ifdef HAVE_AV_AUDIO_CONVERT if (convert_ctx) { av_audio_convert_free(convert_ctx); } #endif return ok; } int fpcalc_main(int argc, char **argv) { int i, j, max_length = 120, num_file_names = 0, raw = 0, raw_fingerprint_size, duration; int16_t *buffer1, *buffer2; int32_t *raw_fingerprint; char *file_name, *fingerprint, **file_names; ChromaprintContext *chromaprint_ctx; int algo = CHROMAPRINT_ALGORITHM_DEFAULT; file_names = malloc(argc * sizeof(char *)); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!strcmp(arg, "-length") && i + 1 < argc) { max_length = atoi(argv[++i]); } else if (!strcmp(arg, "-version") || !strcmp(arg, "-v")) { printf("fpcalc version %s\n", chromaprint_get_version()); return 0; } else if (!strcmp(arg, "-raw")) { raw = 1; } else if (!strcmp(arg, "-algo") && i + 1 < argc) { const char *v = argv[++i]; if (!strcmp(v, "test1")) { algo = CHROMAPRINT_ALGORITHM_TEST1; } else if (!strcmp(v, "test2")) { algo = CHROMAPRINT_ALGORITHM_TEST2; } else if (!strcmp(v, "test3")) { algo = CHROMAPRINT_ALGORITHM_TEST3; } else if (!strcmp(v, "test4")) { algo = CHROMAPRINT_ALGORITHM_TEST4; } else { fprintf(stderr, "WARNING: unknown algorithm, using the default\n"); } } else if (!strcmp(arg, "-set") && i + 1 < argc) { i += 1; } else { file_names[num_file_names++] = argv[i]; } } if (!num_file_names) { printf("usage: %s [OPTIONS] FILE...\n\n", argv[0]); printf("Options:\n"); printf(" -version print version information\n"); printf(" -length SECS length of the audio data used for fingerprint calculation (default 120)\n"); printf(" -raw output the raw uncompressed fingerprint\n"); printf(" -algo NAME version of the fingerprint algorithm\n"); return 2; } av_register_all(); av_log_set_level(AV_LOG_ERROR); buffer1 = av_malloc(BUFFER_SIZE + 16); buffer2 = av_malloc(BUFFER_SIZE + 16); chromaprint_ctx = chromaprint_new(algo); for (i = 1; i < argc; i++) { char *arg = argv[i]; if (!strcmp(arg, "-set") && i + 1 < argc) { char *name = argv[++i]; char *value = strchr(name, '='); if (value) { *value++ = '\0'; chromaprint_set_option(chromaprint_ctx, name, atoi(value)); } } } for (i = 0; i < num_file_names; i++) { file_name = file_names[i]; if (!decode_audio_file(chromaprint_ctx, buffer1, buffer2, file_name, max_length, &duration)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } if (i > 0) { printf("\n"); } printf("FILE=%s\n", file_name); printf("DURATION=%d\n", duration); if (raw) { if (!chromaprint_get_raw_fingerprint(chromaprint_ctx, (void **)&raw_fingerprint, &raw_fingerprint_size)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } printf("FINGERPRINT="); for (j = 0; j < raw_fingerprint_size; j++) { printf("%d%s", raw_fingerprint[j], j + 1 < raw_fingerprint_size ? "," : ""); } printf("\n"); chromaprint_dealloc(raw_fingerprint); } else { if (!chromaprint_get_fingerprint(chromaprint_ctx, &fingerprint)) { fprintf(stderr, "ERROR: unable to calculate fingerprint for file %s, skipping\n", file_name); continue; } printf("FINGERPRINT=%s\n", fingerprint); chromaprint_dealloc(fingerprint); } } chromaprint_free(chromaprint_ctx); av_free(buffer1); av_free(buffer2); free(file_names); return 0; }
Chroma::Result Chroma::operator() (const QString& filename) { std::shared_ptr<AVFormatContext> formatCtx; { AVFormatContext *formatCtxRaw = nullptr; if (avformat_open_input (&formatCtxRaw, filename.toLatin1 ().constData (), nullptr, nullptr)) throw std::runtime_error ("error opening file"); formatCtx.reset (formatCtxRaw, [] (AVFormatContext *ctx) { avformat_close_input (&ctx); }); } { QMutexLocker locker (&CodecMutex_); if (avformat_find_stream_info (formatCtx.get (), nullptr) < 0) throw std::runtime_error ("could not find stream"); } AVCodec *codec = nullptr; const auto streamIndex = av_find_best_stream (formatCtx.get (), AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); if (streamIndex < 0) throw std::runtime_error ("could not find audio stream"); auto stream = formatCtx->streams [streamIndex]; bool codecOpened = false; std::shared_ptr<AVCodecContext> codecCtx (stream->codec, [&codecOpened] (AVCodecContext *ctx) { if (codecOpened) avcodec_close (ctx); }); { QMutexLocker locker (&CodecMutex_); if (avcodec_open2 (codecCtx.get (), codec, nullptr) < 0) throw std::runtime_error ("couldn't open the codec"); } codecOpened = true; if (codecCtx->channels <= 0) throw std::runtime_error ("no channels found"); std::shared_ptr<SwrContext> swr; if (codecCtx->sample_fmt != AV_SAMPLE_FMT_S16) { swr.reset (swr_alloc (), [] (SwrContext *ctx) { if (ctx) swr_free (&ctx); }); av_opt_set_int (swr.get (), "in_channel_layout", codecCtx->channel_layout, 0); av_opt_set_int (swr.get (), "out_channel_layout", codecCtx->channel_layout, 0); av_opt_set_int (swr.get (), "in_sample_rate", codecCtx->sample_rate, 0); av_opt_set_int (swr.get (), "out_sample_rate", codecCtx->sample_rate, 0); av_opt_set_sample_fmt (swr.get (), "in_sample_fmt", codecCtx->sample_fmt, 0); av_opt_set_sample_fmt (swr.get (), "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); swr_init (swr.get ()); } AVPacket packet; av_init_packet (&packet); const int maxLength = 120; auto remaining = maxLength * codecCtx->channels * codecCtx->sample_rate; chromaprint_start (Ctx_, codecCtx->sample_rate, codecCtx->channels); std::shared_ptr<AVFrame> frame (av_frame_alloc (), [] (AVFrame *frame) { av_frame_free (&frame); }); auto maxDstNbSamples = 0; uint8_t *dstData [1] = { nullptr }; std::shared_ptr<void> dstDataGuard (nullptr, [&dstData] (void*) { if (dstData [0]) av_freep (&dstData [0]); }); while (true) { if (av_read_frame (formatCtx.get (), &packet) < 0) break; std::shared_ptr<void> guard (nullptr, [&packet] (void*) { if (packet.data) av_free_packet (&packet); }); if (packet.stream_index != streamIndex) continue; av_frame_unref (frame.get ()); int gotFrame = false; auto consumed = avcodec_decode_audio4 (codecCtx.get (), frame.get (), &gotFrame, &packet); if (consumed < 0 || !gotFrame) continue; uint8_t **data = nullptr; if (swr) { if (frame->nb_samples > maxDstNbSamples) { if (dstData [0]) av_freep (&dstData [0]); int linesize = 0; if (av_samples_alloc (dstData, &linesize, codecCtx->channels, frame->nb_samples, AV_SAMPLE_FMT_S16, 1) < 0) throw std::runtime_error ("cannot allocate memory for resampling"); } if (swr_convert (swr.get (), dstData, frame->nb_samples, const_cast<const uint8_t**> (frame->data), frame->nb_samples) < 0) throw std::runtime_error ("cannot resample audio"); data = dstData; } else data = frame->data; auto length = std::min (remaining, frame->nb_samples * codecCtx->channels); if (!chromaprint_feed (Ctx_, data [0], length)) throw std::runtime_error ("cannot feed data"); bool finished = false; if (maxLength) { remaining -= length; if (remaining <= 0) finished = true; } if (finished) break; } if (!chromaprint_finish (Ctx_)) throw std::runtime_error ("fingerprint calculation failed"); char *fingerprint = 0; if (!chromaprint_get_fingerprint (Ctx_, &fingerprint)) throw std::runtime_error ("unable to get fingerprint"); QByteArray result (fingerprint); chromaprint_dealloc (fingerprint); const double divideFactor = 1. / av_q2d (stream->time_base); const double duration = stream->duration / divideFactor; return { result, static_cast<int> (duration) }; }
int decode_audio_file(ChromaprintContext *chromaprint_ctx, const char *file_name, int max_length, int *duration) { int ok = 0, remaining, length, consumed, codec_ctx_opened = 0, got_frame, stream_index; AVFormatContext *format_ctx = NULL; AVCodecContext *codec_ctx = NULL; AVCodec *codec = NULL; AVStream *stream = NULL; AVFrame *frame = NULL; #if defined(HAVE_SWRESAMPLE) SwrContext *convert_ctx = NULL; #elif defined(HAVE_AVRESAMPLE) AVAudioResampleContext *convert_ctx = NULL; #else void *convert_ctx = NULL; #endif int max_dst_nb_samples = 0, dst_linsize = 0; uint8_t *dst_data[1] = { NULL }; uint8_t **data; AVPacket packet; if (!strcmp(file_name, "-")) { file_name = "pipe:0"; } if (avformat_open_input(&format_ctx, file_name, NULL, NULL) != 0) { fprintf(stderr, "ERROR: couldn't open the file\n"); goto done; } if (avformat_find_stream_info(format_ctx, NULL) < 0) { fprintf(stderr, "ERROR: couldn't find stream information in the file\n"); goto done; } stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); if (stream_index < 0) { fprintf(stderr, "ERROR: couldn't find any audio stream in the file\n"); goto done; } stream = format_ctx->streams[stream_index]; codec_ctx = stream->codec; codec_ctx->request_sample_fmt = AV_SAMPLE_FMT_S16; if (avcodec_open2(codec_ctx, codec, NULL) < 0) { fprintf(stderr, "ERROR: couldn't open the codec\n"); goto done; } codec_ctx_opened = 1; if (codec_ctx->channels <= 0) { fprintf(stderr, "ERROR: no channels found in the audio stream\n"); goto done; } if (codec_ctx->sample_fmt != AV_SAMPLE_FMT_S16) { int64_t channel_layout = codec_ctx->channel_layout; if (!channel_layout) { channel_layout = get_default_channel_layout(codec_ctx->channels); } #if defined(HAVE_SWRESAMPLE) convert_ctx = swr_alloc_set_opts(NULL, channel_layout, AV_SAMPLE_FMT_S16, codec_ctx->sample_rate, channel_layout, codec_ctx->sample_fmt, codec_ctx->sample_rate, 0, NULL); if (!convert_ctx) { fprintf(stderr, "ERROR: couldn't allocate audio converter\n"); goto done; } if (swr_init(convert_ctx) < 0) { fprintf(stderr, "ERROR: couldn't initialize the audio converter\n"); goto done; } #elif defined(HAVE_AVRESAMPLE) convert_ctx = avresample_alloc_context(); av_opt_set_int(convert_ctx, "out_channel_layout", channel_layout, 0); av_opt_set_int(convert_ctx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); av_opt_set_int(convert_ctx, "out_sample_rate", codec_ctx->sample_rate, 0); av_opt_set_int(convert_ctx, "in_channel_layout", channel_layout, 0); av_opt_set_int(convert_ctx, "in_sample_fmt", codec_ctx->sample_fmt, 0); av_opt_set_int(convert_ctx, "in_sample_rate", codec_ctx->sample_rate, 0); if (!convert_ctx) { fprintf(stderr, "ERROR: couldn't allocate audio converter\n"); goto done; } if (avresample_open(convert_ctx) < 0) { fprintf(stderr, "ERROR: couldn't initialize the audio converter\n"); goto done; } #else fprintf(stderr, "ERROR: unsupported audio format (please build fpcalc with libswresample)\n"); goto done; #endif } if (stream->duration != AV_NOPTS_VALUE) { *duration = stream->time_base.num * stream->duration / stream->time_base.den; } else if (format_ctx->duration != AV_NOPTS_VALUE) { *duration = format_ctx->duration / AV_TIME_BASE; } else { fprintf(stderr, "ERROR: couldn't detect the audio duration\n"); goto done; } remaining = max_length * codec_ctx->channels * codec_ctx->sample_rate; chromaprint_start(chromaprint_ctx, codec_ctx->sample_rate, codec_ctx->channels); frame = av_frame_alloc(); while (1) { if (av_read_frame(format_ctx, &packet) < 0) { break; } if (packet.stream_index == stream_index) { av_frame_unref(frame); got_frame = 0; consumed = avcodec_decode_audio4(codec_ctx, frame, &got_frame, &packet); if (consumed < 0) { fprintf(stderr, "WARNING: error decoding audio\n"); continue; } if (got_frame) { data = frame->data; if (convert_ctx) { if (frame->nb_samples > max_dst_nb_samples) { av_freep(&dst_data[0]); if (av_samples_alloc(dst_data, &dst_linsize, codec_ctx->channels, frame->nb_samples, AV_SAMPLE_FMT_S16, 1) < 0) { fprintf(stderr, "ERROR: couldn't allocate audio converter buffer\n"); goto done; } max_dst_nb_samples = frame->nb_samples; } #if defined(HAVE_SWRESAMPLE) if (swr_convert(convert_ctx, dst_data, frame->nb_samples, (const uint8_t **)frame->data, frame->nb_samples) < 0) #elif defined(HAVE_AVRESAMPLE) if (avresample_convert(convert_ctx, dst_data, 0, frame->nb_samples, (uint8_t **)frame->data, 0, frame->nb_samples) < 0) #endif { fprintf(stderr, "ERROR: couldn't convert the audio\n"); goto done; } data = dst_data; } length = MIN(remaining, frame->nb_samples * codec_ctx->channels); if (!chromaprint_feed(chromaprint_ctx, data[0], length)) { goto done; } if (max_length) { remaining -= length; if (remaining <= 0) { goto finish; } } } } av_free_packet(&packet); } finish: if (!chromaprint_finish(chromaprint_ctx)) { fprintf(stderr, "ERROR: fingerprint calculation failed\n"); goto done; } ok = 1; done: if (frame) { av_frame_free(&frame); } if (dst_data[0]) { av_freep(&dst_data[0]); } if (convert_ctx) { #if defined(HAVE_SWRESAMPLE) swr_free(&convert_ctx); #elif defined(HAVE_AVRESAMPLE) avresample_free(&convert_ctx); #endif } if (codec_ctx_opened) { avcodec_close(codec_ctx); } if (format_ctx) { avformat_close_input(&format_ctx); } return ok; }
QString chromaprinter::calcFingerPrint(SoundSourceProxy& soundSource){ soundSource.open(); m_SampleRate = soundSource.getSampleRate(); unsigned int length = soundSource.length(); if (m_SampleRate == 0 ){ qDebug() << "Skipping invalid file:" << soundSource.getFilename(); return QString(); } // this is worth 2min of audio, multiply by 2 because we have 2 channels // AcoustID only stores a fingerprint for the first two minutes of a song // on their server so we need only a fingerprint of the first two minutes // --kain88 July 2012 m_NumSamples = 120*2*m_SampleRate; // check that the song is actually longer then the amount of audio we use if (m_NumSamples > length) { m_NumSamples = length; } SAMPLE *pData = new SAMPLE[m_NumSamples]; QTime timerReadingFile; timerReadingFile.start(); unsigned int read = soundSource.read(m_NumSamples, pData); if (read!=m_NumSamples) { qDebug() << "oh that's embarrasing I couldn't read the track"; return QString(); } qDebug("reading file took: %d ms" , timerReadingFile.elapsed()); ChromaprintContext* ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT); // we have 2 channels in mixxx always chromaprint_start(ctx, m_SampleRate, 2); QTime timerGeneratingFingerPrint; timerGeneratingFingerPrint.start(); int success = chromaprint_feed(ctx, pData, m_NumSamples); if (!success) { qDebug() << "could not generate fingerprint"; delete [] pData; return QString(); } chromaprint_finish(ctx); void* fprint = NULL; int size = 0; int ret = chromaprint_get_raw_fingerprint(ctx, &fprint, &size); QByteArray fingerprint; if (ret == 1) { void* encoded = NULL; int encoded_size = 0; chromaprint_encode_fingerprint(fprint, size, CHROMAPRINT_ALGORITHM_DEFAULT, &encoded, &encoded_size, 1); fingerprint.append(reinterpret_cast<char*>(encoded), encoded_size); chromaprint_dealloc(fprint); chromaprint_dealloc(encoded); } chromaprint_free(ctx); delete [] pData; qDebug("generating fingerprint took: %d ms" , timerGeneratingFingerPrint.elapsed()); return fingerprint; }
static void *print_thread(void *arg) { struct GrooveFingerprinterPrivate *p = (GrooveFingerprinterPrivate *)arg; struct GrooveFingerprinter *printer = &p->externals; struct GrooveBuffer *buffer; while (!p->abort_request.load()) { pthread_mutex_lock(&p->info_head_mutex); if (p->info_queue_count >= printer->info_queue_size) { pthread_cond_wait(&p->drain_cond, &p->info_head_mutex); pthread_mutex_unlock(&p->info_head_mutex); continue; } // we definitely want to unlock the mutex while we wait for the // next buffer. Otherwise there will be a deadlock when sink_flush or // sink_purge is called. pthread_mutex_unlock(&p->info_head_mutex); int result = groove_sink_buffer_get(p->sink, &buffer, 1); pthread_mutex_lock(&p->info_head_mutex); if (result == GROOVE_BUFFER_END) { // last file info emit_track_info(p); // send album info struct GrooveFingerprinterInfo *info = allocate<GrooveFingerprinterInfo>(1); if (info) { info->duration = p->album_duration; groove_queue_put(p->info_queue, info); } else { av_log(NULL, AV_LOG_ERROR, "unable to allocate album fingerprint info\n"); } p->album_duration = 0.0; p->info_head = NULL; p->info_pos = -1.0; pthread_mutex_unlock(&p->info_head_mutex); continue; } if (result != GROOVE_BUFFER_YES) { pthread_mutex_unlock(&p->info_head_mutex); break; } if (buffer->item != p->info_head) { if (p->info_head) { emit_track_info(p); } if (!chromaprint_start(p->chroma_ctx, 44100, 2)) { av_log(NULL, AV_LOG_ERROR, "unable to start fingerprint\n"); } p->track_duration = 0.0; p->info_head = buffer->item; p->info_pos = buffer->pos; } double buffer_duration = buffer->frame_count / (double)buffer->format.sample_rate; p->track_duration += buffer_duration; p->album_duration += buffer_duration; if (!chromaprint_feed(p->chroma_ctx, buffer->data[0], buffer->frame_count * 2)) { av_log(NULL, AV_LOG_ERROR, "unable to feed fingerprint\n"); } pthread_mutex_unlock(&p->info_head_mutex); groove_buffer_unref(buffer); } return NULL; }