Clementine-audio-player-Mac.../3rdparty/chromaprint/examples/fpcalc.c

304 lines
7.8 KiB
C

#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <chromaprint.h>
#ifdef HAVE_AV_AUDIO_CONVERT
#include "ffmpeg/audioconvert.h"
#include "ffmpeg/samplefmt.h"
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define BUFFER_SIZE (AVCODEC_MAX_AUDIO_FRAME_SIZE * 2)
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 (av_open_input_file(&format_ctx, file_name, NULL, 0, NULL) != 0) {
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 LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 64, 0)
if (codec_ctx && codec_ctx->codec_type == CODEC_TYPE_AUDIO) {
#else
if (codec_ctx && codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
#endif
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 != SAMPLE_FMT_S16) {
#ifdef HAVE_AV_AUDIO_CONVERT
convert_ctx = av_audio_convert_alloc(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, 25, 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 };
int istride[6] = { av_get_bits_per_sample_format(codec_ctx->sample_fmt) / 8 };
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;
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, "-raw")) {
raw = 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(" -length SECS length of the audio data used for fingerprint calculation (default 120)\n");
printf(" -raw output the raw uncompressed fingerprint\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(CHROMAPRINT_ALGORITHM_DEFAULT);
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 ? "," : "\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;
}
#ifdef _WIN32
int main(int win32_argc, char **win32_argv)
{
int i, argc = 0, buffsize = 0, offset = 0;
char **utf8_argv, *utf8_argv_ptr;
wchar_t **argv;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
buffsize = 0;
for (i = 0; i < argc; i++) {
buffsize += WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL);
}
utf8_argv = av_mallocz(sizeof(char *) * (argc + 1) + buffsize);
utf8_argv_ptr = (char *)utf8_argv + sizeof(char *) * (argc + 1);
for (i = 0; i < argc; i++) {
utf8_argv[i] = &utf8_argv_ptr[offset];
offset += WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, &utf8_argv_ptr[offset], buffsize - offset, NULL, NULL);
}
LocalFree(argv);
return fpcalc_main(argc, utf8_argv);
}
#else
int main(int argc, char **argv)
{
return fpcalc_main(argc, argv);
}
#endif