audiolib/audio/audio_demuxer_mp3.c

261 lines
10 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "audio.h"
#include "audio_internal.h"
#include "audio_demuxer_mp3.h"
typedef struct _DemuxerMP3Context {
uint32_t outputBufferPosition;
uint32_t missingBytesInLastPacket;
AudioBuffer *outputBuffer;
uint8_t headerData[4];
uint8_t headerIndex;
} DemuxerMP3Context;
static uint32_t const bitrateTable[5][16] = {
/* MPEG-1 */
{ 0, 32000, 64000, 96000, 128000, 160000, 192000, 224000, /* Layer I */
256000, 288000, 320000, 352000, 384000, 416000, 448000, 0 },
{ 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer II */
128000, 160000, 192000, 224000, 256000, 320000, 384000, 0 },
{ 0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, /* Layer III */
112000, 128000, 160000, 192000, 224000, 256000, 320000, 0 },
/* MPEG-2 LSF */
{ 0, 32000, 48000, 56000, 64000, 80000, 96000, 112000, /* Layer I */
128000, 144000, 160000, 176000, 192000, 224000, 256000, 0 },
{ 0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, /* Layers II & III */
64000, 80000, 96000, 112000, 128000, 144000, 160000, 0 }
};
static uint32_t const sampleRateTable[3][3] = {
/* MPEG-1 */
{ 44100, 48000, 32000 },
/* MPEG-2 */
{ 22050, 24000, 16000 },
/* MPEG-2.5 */
{ 11025, 12000, 8000 }
};
MP3Header demuxer_mp3_decode_header(uint8_t data[4]) {
MP3Header header = { 0 };
header.version = (data[1] >> 3) & 0x03;
header.layer = (data[1] >> 1) & 0x03;
header.has_crc = (data[1] & 0x01) == 0;
header.bitrateIndex = (data[2] >> 4) & 0x0f;
header.sampleRateIndex = (data[2] >> 2) & 0x03;
header.has_padding = (data[2] >> 1) & 0x01;
header.is_private = data[2] & 0x01;
header.channelMode = (data[3] >> 6) & 0x03;
header.jointStereoModeExtension = (data[3] >> 4) & 0x03;
header.has_copyright = (data[3] >> 3) & 0x01;
header.is_original = (data[3] >> 2) & 0x01;
header.emphasis = data[3] & 0x03;
header.valid = (header.version != MPEGVersionReserved) && (header.layer != MPEGLayerReserved) && (header.bitrateIndex != 0x0f) && (header.sampleRateIndex != 0x03);
if (header.valid) {
if (header.version == MPEGVersion1) {
switch (header.layer) {
case MPEGLayer1:
header.bitrate = bitrateTable[0][header.bitrateIndex];
break;
case MPEGLayer2:
header.bitrate = bitrateTable[1][header.bitrateIndex];
break;
case MPEGLayer3:
header.bitrate = bitrateTable[2][header.bitrateIndex];
break;
case MPEGLayerReserved:
break;
}
}
if ((header.version == MPEGVersion2) || (header.version == MPEGVersion2_5)) {
switch (header.layer) {
case MPEGLayer1:
header.bitrate = bitrateTable[3][header.bitrateIndex];
break;
case MPEGLayer2:
case MPEGLayer3:
header.bitrate = bitrateTable[4][header.bitrateIndex];
break;
case MPEGLayerReserved:
break;
}
}
switch (header.version) {
case MPEGVersion1:
header.sampleRate = sampleRateTable[0][header.sampleRateIndex];
break;
case MPEGVersion2:
header.sampleRate = sampleRateTable[1][header.sampleRateIndex];
break;
case MPEGVersion2_5:
header.sampleRate = sampleRateTable[2][header.sampleRateIndex];
break;
case MPEGVersionReserved:
break;
}
header.packetLength = 144 * ((float)header.bitrate / (float)header.sampleRate);
if (header.has_padding) {
if (header.layer == MPEGLayer1) {
header.packetLength += 4;
} else {
header.packetLength += 1;
}
}
if (header.has_crc) {
header.packetLength += 2;
}
header.packetLength -= 4; // Header
//fprintf(stderr, "INFO: Packet length: %d\n", header.packetLength);
if ((header.packetLength > 2016) || (header.packetLength < 96)) {
header.valid = false;
}
} else {
fprintf(stderr, "Invalid header!\n");
}
return header;
}
static inline AudioPipelineStatus demuxer_mp3_emit(AudioPipelineElement *self) {
DemuxerMP3Context *context = (DemuxerMP3Context *)self->ctx;
//fprintf(stderr, "INFO: Emitting packet of size %d\n", context->outputBufferPosition);
uint32_t buf_sz = context->outputBuffer->buf_size;
context->outputBuffer->buf_size = context->outputBufferPosition;
AudioPipelineStatus result = self->next->push(self->next, context->outputBuffer);
context->outputBuffer->buf_size = buf_sz;
context->outputBufferPosition = 0;
return result;
}
AudioPipelineStatus demuxer_mp3_push(AudioPipelineElement *self, AudioBuffer *buffer) {
DemuxerMP3Context *context = (DemuxerMP3Context *)self->ctx;
uint32_t start = 0;
bool sync = true;
if (context->missingBytesInLastPacket) {
// FIXME: what if the buffer is too small
if (buffer->buf_size < context->missingBytesInLastPacket) {
uint32_t remaining_bytes = (context->missingBytesInLastPacket - buffer->buf_size);
//fprintf(stderr, "INFO: Last buffer was too short, copying %d bytes, %d remaining \n", buffer->buf_size, remaining_bytes);
memcpy(context->outputBuffer->data + context->outputBufferPosition, buffer->data, buffer->buf_size);
context->outputBufferPosition += buffer->buf_size;
context->missingBytesInLastPacket = remaining_bytes;
return PipelineBuffering;
}
//fprintf(stderr, "INFO: Last buffer was too short, copying %d bytes\n", context->missingBytesInLastPacket);
memcpy(context->outputBuffer->data + context->outputBufferPosition, buffer->data, context->missingBytesInLastPacket);
context->outputBufferPosition += context->missingBytesInLastPacket;
demuxer_mp3_emit(self);
start = context->missingBytesInLastPacket;
context->missingBytesInLastPacket = 0;
}
for (uint32_t i = start; i < buffer->buf_size; i++) {
switch (context->headerIndex) {
case 0:
if (buffer->data[i] != 0xff) {
if (sync) {
fprintf(stderr, "WARN: Sync lost at %d\n", i);
sync = false;
}
continue;
}
context->headerData[context->headerIndex++] = buffer->data[i];
continue;
case 1:
if (buffer->data[i] < 0xe0) {
context->headerIndex = 0;
continue;
}
context->headerData[context->headerIndex++] = buffer->data[i];
continue;
case 2:
case 3:
context->headerData[context->headerIndex++] = buffer->data[i];
continue;
default:
break;
}
context->headerIndex = 0;
// sync marker found, try to decode the header
MP3Header header = demuxer_mp3_decode_header(context->headerData);
if (!header.valid) {
continue;
}
sync = true;
if (i + header.packetLength < buffer->buf_size) {
// FIXME: realloc output Buffer size if too small
//fprintf(stderr, "INFO: Found frame sync at %d, copying %d bytes\n", i, header.packetLength);
memcpy(context->outputBuffer->data + context->outputBufferPosition, context->headerData, 4);
context->outputBufferPosition += 4;
memcpy(context->outputBuffer->data + context->outputBufferPosition, buffer->data + i, header.packetLength);
i += header.packetLength - 1;
context->outputBufferPosition += header.packetLength;
demuxer_mp3_emit(self);
} else {
uint32_t remaining_bytes = (buffer->buf_size - i);
//fprintf(stderr, "INFO: Found frame sync at %d, buffer too short copying %d bytes\n", i, remaining_bytes);
memcpy(context->outputBuffer->data + context->outputBufferPosition, context->headerData, 4);
context->outputBufferPosition += 4;
memcpy(context->outputBuffer->data + context->outputBufferPosition, buffer->data + i, remaining_bytes);
context->outputBufferPosition += remaining_bytes;
context->missingBytesInLastPacket = header.packetLength - remaining_bytes;
return PipelineBuffering;
}
}
return PipelineRunning;
}
AudioPipelineStatus demuxer_mp3_link(AudioPipelineElement *self, AudioPipelineElement *source) {
if ((source->sample_rate != 0) || (source->channels != 0) || (source->bits_per_sample != 0) || (source->type != AudioElementSource)) {
fprintf(stderr, "ERROR: MP3 demuxer can only link to a data source, not %s!\n", source->describe(source));
return PipelineError;
}
source->next = self;
return PipelineStopped;
}
char *demuxer_mp3_describe(AudioPipelineElement *self) {
return "mp3 demuxer";
}
void demuxer_mp3_destroy(AudioPipelineElement *self) {
DemuxerMP3Context *context = (DemuxerMP3Context *)self->ctx;
if (context->outputBuffer) {
free(context->outputBuffer);
}
free(context);
free(self);
}
AudioPipelineElement *audio_demuxer_mp3(void) {
AudioPipelineElement *self = calloc(1, sizeof(AudioPipelineElement));
DemuxerMP3Context *context = calloc(1, sizeof(DemuxerMP3Context));
context->outputBuffer = alloc_audio_buffer(2048);
self->ctx = context;
self->describe = demuxer_mp3_describe;
self->start = filter_start_nop;
self->reset = filter_reset_nop;
self->stop = filter_stop_nop;
self->push = demuxer_mp3_push;
self->link = demuxer_mp3_link;
self->destroy = demuxer_mp3_destroy;
self->type = AudioElementDemuxer;
return self;
}