#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "format_detect.h"
#include "log.h"
#include "misc.h"
#define OGG_PAGE_HEADER_SIZE 28 /* cf. rfc3533 */
#define OGG_MAGIC "OggS" /* cf. rfc3533 */
#define OGG_VORBIS_IDENT "\x01vorbis" /* cf. rfc5334 */
#define OGG_OPUS_IDENT "OpusHead" /* cf. rfc7845 */
#define OGG_FLAC_IDENT "\177FLAC" /* cf. rfc5334 */
#define OGG_IDENT_MAX_SIZE 8
/*cf. https://xiph.org/flac/format.html */
#define FLAC_IDENT "fLaC"
static inline int memcmp_str(const void *s1, const char *s2)
{
return memcmp(s1, s2, strlen(s2));
}
Audio_format detect_audio_format(const char *path)
{
int fd = xopen(path, O_RDONLY);
static char buf[OGG_PAGE_HEADER_SIZE + OGG_IDENT_MAX_SIZE];
const char *ext = strrchr(path, '.');
if (!strcmp(ext, ".ogg"))
{
if (!read_full(fd, buf, OGG_PAGE_HEADER_SIZE + OGG_IDENT_MAX_SIZE))
LOG_DIE("%s: %s", path, strerror(errno));
if (!memcmp_str(buf, OGG_MAGIC)) /* Try vorbis, opus then flac */
{
if (!memcmp_str(buf + OGG_PAGE_HEADER_SIZE, OGG_VORBIS_IDENT))
return AUDIO_FORMAT_OGG_VORBIS;
if (!memcmp_str(buf + OGG_PAGE_HEADER_SIZE, OGG_OPUS_IDENT))
return AUDIO_FORMAT_OGG_OPUS;
if (!memcmp_str(buf + OGG_PAGE_HEADER_SIZE, OGG_FLAC_IDENT))
return AUDIO_FORMAT_OGG_FLAC;
log_append(LOG_ERR, "%s: ogg file with unknown codec", path);
return AUDIO_FORMAT_UNKNOWN;
}
log_append(LOG_ERR, "%s: ogg extension but not an ogg file", path);
return AUDIO_FORMAT_UNKNOWN;
}
if (!strcmp(ext, ".opus"))
{
if (!read_full(fd, buf, OGG_PAGE_HEADER_SIZE + OGG_IDENT_MAX_SIZE))
LOG_DIE("%s: %s", path, strerror(errno));
if (!memcmp_str(buf, OGG_MAGIC))
{
if (!memcmp_str(buf + OGG_PAGE_HEADER_SIZE, OGG_OPUS_IDENT))
return AUDIO_FORMAT_OGG_OPUS;
log_append(LOG_ERR, "%s: ogg file with unknown codec", path);
return AUDIO_FORMAT_UNKNOWN;
}
log_append(LOG_ERR, "%s: opus extension but not an ogg file", path);
return AUDIO_FORMAT_UNKNOWN;
}
if (!strcmp(ext, ".flac"))
{
if (!read_full(fd, buf, strlen(FLAC_IDENT)))
LOG_DIE("%s: %s", path, strerror(errno));
if (!memcmp_str(buf, FLAC_IDENT))
return AUDIO_FORMAT_FLAC;
log_append(LOG_ERR, "%s: flac extension but not a flac file", path);
return AUDIO_FORMAT_UNKNOWN;
}
xclose(fd);
return AUDIO_FORMAT_UNKNOWN;
}