#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include "decoder.h"
#include "duration.h"
#include "format_detect.h"
#include "log.h"
#include "misc.h"
#ifdef USE_FLAC
#include "flac.h"
#endif
#ifdef USE_OGG_VORBIS
#include "vorbis.h"
#endif
#ifdef USE_OGG_OPUS
#include "opus.h"
#endif
#if !defined (USE_FLAC) || !defined (USE_OGG_VORBIS) || !defined (USE_OGG_OPUS)
static void * unsupported(int fd, Decoder *dec)
{
(void)fd;
log_append(LOG_ERR, "%s: %s support isn't enabled",
dec->tinfo->path, audio_format_str[dec->tinfo->aufmt]);
return NULL;
}
#endif
static void *(*decoder_init_func[NUM_AUDIO_FORMAT])(int, Decoder *) =
{
#ifdef USE_FLAC
flac_init,
flac_init, /* USE_FLAC contains both FLAC and Ogg FLAC support */
#else
unsupported,
unsupported,
#endif
#ifdef USE_OGG_VORBIS
vorbis_init,
#else
unsupported,
#endif
#ifdef USE_OGG_OPUS
opus_init,
#else
unsupported,
#endif
};
static void (*decoder_free_func[NUM_AUDIO_FORMAT])(void *) =
{
#ifdef USE_FLAC
flac_free,
flac_free,
#else
NULL,
NULL,
#endif
#ifdef USE_OGG_VORBIS
vorbis_free,
#else
NULL,
#endif
#ifdef USE_OGG_OPUS
opus_free,
#else
NULL,
#endif
};
static void *(*decoder_routine_func[NUM_AUDIO_FORMAT])(void *) =
{
#ifdef USE_FLAC
flac_routine,
flac_routine,
#else
NULL,
NULL,
#endif
#ifdef USE_OGG_VORBIS
vorbis_routine,
#else
NULL,
#endif
#ifdef USE_OGG_OPUS
opus_routine,
#else
NULL,
#endif
};
/* wrapper around open -> fadvise */
static int open_fadv(const char *path)
{
int fd = open(path, O_RDONLY | O_CLOEXEC);
if (fd == -1)
{
log_append(LOG_ERR, "%s: %s", path, strerror(errno));
return -1;
}
int err = posix_fadvise(fd, 0, 0,
POSIX_FADV_SEQUENTIAL | POSIX_FADV_WILLNEED);
if (err != 0)
log_append(LOG_ERR, "%s: %s", path, strerror(err));
return fd;
}
bool decoder_init(Decoder *dec, int pipefd_wr, Track_info *tinfo,
ReplayGain_type rgtype, const char *next)
{
if (access(tinfo->path, R_OK))
{
log_append(LOG_ERR, "%s: file not found or not readable", tinfo->path);
return false;
}
if (dec->next_fd != -1)
dec->cur_fd = dec->next_fd;
else
{
dec->cur_fd = open_fadv(tinfo->path);
if (dec->cur_fd == -1)
return false;
}
dec->next_fd = next ? open_fadv(next) : -1;
dec->pipefd_wr = pipefd_wr;
dec->tinfo = tinfo;
dec->tinfo->aufmt = detect_audio_format(tinfo->path);
dec->tinfo->duration = NULL;
dec->tinfo->position = NULL;
dec->rgtype = rgtype;
dec->cancel_flag = false;
tinfo->tags = tags_init();
dec->ctx = decoder_init_func[dec->tinfo->aufmt](dec->cur_fd, dec);
if (!dec->ctx)
{
decoder_clear(dec);
return false;
}
dec->tinfo->gain = tag_compute_replay_gain(tinfo->tags, rgtype);
return true;
}
void decoder_clear(const Decoder *dec)
{
if (dec->ctx)
decoder_free_func[dec->tinfo->aufmt](dec->ctx);
tags_free(dec->tinfo->tags);
duration_free(dec->tinfo->duration);
duration_free(dec->tinfo->position);
}
void decoder_end(const Decoder *dec)
{
xclose(dec->pipefd_wr);
}
bool decoder_spawn_thread(Decoder *dec)
{
int err = pthread_create(&dec->tid, NULL,
decoder_routine_func[dec->tinfo->aufmt], dec);
if (err)
log_append(LOG_ERR, "Decoding thread: %s", strerror(err));
return !err;
}