Enabled APE support and reworked FFMPEG decoder according to the mamburu fork

CQTexperiment
Chris Moeller 2013-10-10 20:55:32 -07:00
parent 84474c5002
commit 51dc6fae15
2 changed files with 178 additions and 186 deletions

View File

@ -16,26 +16,24 @@
@interface FFMPEGDecoder : NSObject <CogDecoder> @interface FFMPEGDecoder : NSObject <CogDecoder>
{ {
id<CogSource> source;
void *sampleBuffer;
int sampleBufferSize;
int numFrames;
int samplePos;
int streamIndex;
AVFormatContext *ic;
AVCodecContext *c;
AVCodec *codec;
BOOL seekable; BOOL seekable;
BOOL floatingPoint;
int bitsPerSample;
int bitrate;
int channels; int channels;
int bitsPerSample;
BOOL floatingPoint;
float frequency; float frequency;
long totalFrames; long totalFrames;
long framesPlayed; long framesRead;
long framesToSkip; int bitrate;
@private
int streamIndex;
AVFormatContext *formatCtx;
AVCodecContext *codecCtx;
AVFrame *lastDecodedFrame;
AVPacket *lastReadPacket;
int bytesConsumedFromDecodedFrame;
int bytesReadFromPacket;
BOOL readNextPacket;
} }
@end @end

View File

@ -45,37 +45,45 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
+ (void)initialize + (void)initialize
{ {
if(self == [FFMPEGDecoder class])
{
av_log_set_flags(AV_LOG_SKIP_REPEATED);
av_log_set_level(AV_LOG_ERROR);
av_register_all(); av_register_all();
registerCogProtocols(); registerCogProtocols();
av_lockmgr_register(lockmgr_callback); av_lockmgr_register(lockmgr_callback);
}
} }
- (BOOL)open:(id<CogSource>)s - (BOOL)open:(id<CogSource>)s
{ {
source = [s retain]; int errcode, i;
const char *filename = [[[[s url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String];
formatCtx = NULL;
totalFrames = 0;
framesRead = 0;
int err, i;
const char *filename = [[[[source url] absoluteString] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] UTF8String];
ic = NULL;
numFrames = 0;
samplePos = 0;
sampleBuffer = NULL;
// register all available codecs // register all available codecs
err = avformat_open_input(&ic, filename, NULL, NULL); if ((errcode = avformat_open_input(&formatCtx, filename, NULL, NULL)) < 0)
if (err < 0)
{ {
NSLog(@"Opening file failed horribly: %d", err); char errDescr[4096];
av_strerror(errcode, errDescr, 4096);
NSLog(@"ERROR OPENING FILE, errcode = %d, error = %s", errcode, errDescr);
return NO;
}
if(avformat_find_stream_info(formatCtx, NULL) < 0)
{
NSLog(@"CAN'T FIND STREAM INFO!");
return NO; return NO;
} }
streamIndex = -1; streamIndex = -1;
for(i = 0; i < ic->nb_streams; i++) { for(i = 0; i < formatCtx->nb_streams; i++) {
c = ic->streams[i]->codec; codecCtx = formatCtx->streams[i]->codec;
if(c->codec_type == AVMEDIA_TYPE_AUDIO) if(codecCtx->codec_type == AVMEDIA_TYPE_AUDIO)
{ {
NSLog(@"audio codec found"); NSLog(@"audio codec found");
streamIndex = i; streamIndex = i;
@ -88,49 +96,29 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
return NO; return NO;
} }
avformat_find_stream_info(ic, NULL); AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
codec = avcodec_find_decoder(c->codec_id);
if (!codec) { if (!codec) {
NSLog(@"codec not found"); NSLog(@"codec not found");
return NO; return NO;
} }
if (avcodec_open2(c, codec, NULL) < 0) { if (avcodec_open2(codecCtx, codec, NULL) < 0) {
NSLog(@"could not open codec"); NSLog(@"could not open codec");
return NO; return NO;
} }
av_dump_format(ic, 0, filename, 0); lastDecodedFrame = avcodec_alloc_frame();
avcodec_get_frame_defaults(lastDecodedFrame);
lastReadPacket = malloc(sizeof(AVPacket));
av_new_packet(lastReadPacket, 0);
readNextPacket = YES;
bytesConsumedFromDecodedFrame = 0;
AVDictionary * metadata = ic->metadata; frequency = codecCtx->sample_rate;
AVDictionaryEntry * entry; channels = codecCtx->channels;
if ((entry = av_dict_get(metadata, "title", NULL, 0)))
NSLog(@"Title: %s", entry->value);
if ((entry = av_dict_get(metadata, "author", NULL, 0)))
NSLog(@"Author: %s", entry->value);
if ((entry = av_dict_get(metadata, "album", NULL, 0)))
NSLog(@"Album: %s", entry->value);
if ((entry = av_dict_get(metadata, "year", NULL, 0)))
NSLog(@"Year: %s", entry->value);
if ((entry = av_dict_get(metadata, "track", NULL, 0)))
NSLog(@"Track: %s", entry->value);
if ((entry = av_dict_get(metadata, "genre", NULL, 0)))
NSLog(@"Genre: %s", entry->value);
if ((entry = av_dict_get(metadata, "copyright", NULL, 0)))
NSLog(@"Copyright: %s", entry->value);
if ((entry = av_dict_get(metadata, "comment", NULL, 0)))
NSLog(@"Comments: %s", entry->value);
NSLog(@"bitrate: %d", ic->bit_rate);
NSLog(@"sample rate: %d", c->sample_rate);
NSLog(@"channels: %d", c->channels);
channels = c->channels;
bitrate = c->bit_rate / 1000;
floatingPoint = NO; floatingPoint = NO;
switch (c->sample_fmt) {
switch (codecCtx->sample_fmt) {
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_U8P:
bitsPerSample = 8; bitsPerSample = 8;
@ -161,135 +149,141 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
default: default:
return NO; return NO;
} }
totalFrames = c->sample_rate * ((float)ic->duration/AV_TIME_BASE);
framesPlayed = 0; totalFrames = codecCtx->sample_rate * ((float)formatCtx->duration/AV_TIME_BASE);
frequency = c->sample_rate; bitrate = (codecCtx->bit_rate) / 1000;
seekable = YES; framesRead = 0;
seekable = [s seekable];
return YES; return YES;
} }
- (void)close - (void)close
{ {
avcodec_close(c); if (lastReadPacket)
avformat_close_input(&ic); {
av_free(sampleBuffer); av_free_packet(lastReadPacket);
free(lastReadPacket);
lastReadPacket = NULL;
}
[source close]; if (lastDecodedFrame) { av_free(lastDecodedFrame); lastDecodedFrame = NULL; }
[source release];
if (codecCtx) { avcodec_close(codecCtx); codecCtx = NULL; }
if (formatCtx) { avformat_close_input(&(formatCtx)); formatCtx = NULL; }
} }
- (int)readAudio:(void *)buf frames:(UInt32)frames - (int)readAudio:(void *)buf frames:(UInt32)frames
{ {
AVPacket framePacket; int frameSize = channels * (bitsPerSample / 8);
int framesRead = 0; int gotFrame = 0;
int dataSize = 0;
int bytesPerFrame = (bitsPerSample/8) * channels; int bytesToRead = frames * frameSize;
int bytesRead = 0;
while (frames > 0) int8_t* targetBuf = (int8_t*) buf;
memset(buf, 0, bytesToRead);
while (bytesRead < bytesToRead)
{ {
if (samplePos < numFrames)
if(readNextPacket)
{ {
int samplesLeft; // consume next chunk of encoded data from input stream
samplesLeft = numFrames - samplePos; av_free_packet(lastReadPacket);
if(av_read_frame(formatCtx, lastReadPacket) < 0)
if (samplesLeft > frames)
samplesLeft = frames;
memcpy(buf, sampleBuffer + (samplePos * bytesPerFrame), samplesLeft * bytesPerFrame);
buf += samplesLeft * bytesPerFrame;
framesRead += samplesLeft;
frames -= samplesLeft;
samplePos += samplesLeft;
}
if (frames > 0)
{ {
if (framesPlayed >= totalFrames) NSLog(@"End of stream");
break; break; // end of stream;
size_t sampleBufferOffset = 0;
if (av_read_frame(ic, &framePacket) < 0)
{
NSLog(@"Uh oh... av_read_frame returned negative");
break;
} }
if ( framePacket.stream_index != streamIndex ) readNextPacket = NO; // we probably won't need to consume another chunk
{ bytesReadFromPacket = 0; // until this one is fully decoded
av_free_packet( &framePacket );
continue;
} }
AVFrame * frame = av_frame_alloc(); // buffer size needed to hold decoded samples, in bytes
int ret, got_frame = 0; int planeSize;
int planar = av_sample_fmt_is_planar(codecCtx->sample_fmt);
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
lastDecodedFrame->nb_samples,
codecCtx->sample_fmt, 1);
while ( framePacket.size && (ret = avcodec_decode_audio4(c, frame, &got_frame, &framePacket)) >= 0 ) if (dataSize <= bytesConsumedFromDecodedFrame)
{ {
ret = FFMIN(ret, framePacket.size); // consumed all decoded samples - decode more
framePacket.data += ret; avcodec_get_frame_defaults(lastDecodedFrame);
framePacket.size -= ret; bytesConsumedFromDecodedFrame = 0;
int len = avcodec_decode_audio4(codecCtx, lastDecodedFrame, &gotFrame, lastReadPacket);
if (len < 0 || (!gotFrame))
{
char errbuf[4096];
av_strerror(len, errbuf, 4096);
NSLog(@"Error decoding: len = %d, gotFrame = %d, strerr = %s", len, gotFrame, errbuf);
if ( !got_frame ) continue; dataSize = 0;
readNextPacket = YES;
int plane_size;
int planar = av_sample_fmt_is_planar(c->sample_fmt);
int data_size = av_samples_get_buffer_size(&plane_size, c->channels,
frame->nb_samples,
c->sample_fmt, 1);
sampleBuffer = av_realloc(sampleBuffer, sampleBufferOffset + data_size);
if (!planar) {
memcpy((uint8_t *)sampleBuffer + sampleBufferOffset, frame->extended_data[0], plane_size);
} }
else if (channels > 1) { else
uint8_t * out = (uint8_t *)sampleBuffer + sampleBufferOffset; {
// Something has been successfully decoded
dataSize = av_samples_get_buffer_size(NULL, codecCtx->channels,
lastDecodedFrame->nb_samples,
codecCtx->sample_fmt, 1);
bytesReadFromPacket += len;
}
if (bytesReadFromPacket >= lastReadPacket->size)
{
// decoding consumed all the read packet - read another next time
readNextPacket = YES;
}
}
int toConsume = FFMIN((dataSize - bytesConsumedFromDecodedFrame), (bytesToRead - bytesRead));
// copy decoded samples to Cog's buffer
if (!planar || channels == 1) {
memmove(targetBuf + bytesRead, (lastDecodedFrame->data[0] + bytesConsumedFromDecodedFrame), toConsume);
}
else {
uint8_t * out = ( uint8_t * ) targetBuf + bytesRead;
int bytesPerSample = bitsPerSample / 8; int bytesPerSample = bitsPerSample / 8;
for (int s = 0; s < plane_size; s += bytesPerSample) { int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / bytesPerSample;
int toConsumePerPlane = toConsume / channels;
for (int s = 0; s < toConsumePerPlane; s += bytesPerSample) {
for (int ch = 0; ch < channels; ++ch) { for (int ch = 0; ch < channels; ++ch) {
memcpy(out, frame->extended_data[ch] + s, bytesPerSample); memcpy(out, lastDecodedFrame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
out += bytesPerSample; out += bytesPerSample;
} }
} }
} }
sampleBufferOffset += plane_size * channels; bytesConsumedFromDecodedFrame += toConsume;
bytesRead += toConsume;
} }
av_frame_free(&frame); int framesReadNow = bytesRead / frameSize;
if ( framesRead + framesReadNow > totalFrames )
framesReadNow = totalFrames - framesRead;
if (framePacket.data) framesRead += framesReadNow;
av_free_packet(&framePacket);
if ( !sampleBufferOffset ) {
if ( ret < 0 ) break;
else continue;
}
numFrames = sampleBufferOffset / bytesPerFrame;
samplePos = 0;
if (numFrames + framesPlayed > totalFrames)
numFrames = totalFrames - framesPlayed;
framesPlayed += numFrames;
}
}
return framesRead;
return framesReadNow;
} }
- (long)seek:(long)frame - (long)seek:(long)frame
{ {
NSLog(@"frame: %ld", frame); if (frame > totalFrames) { return -1; }
AVRational time_base = ic->streams[streamIndex]->time_base; int64_t ts = frame * (formatCtx->duration) / totalFrames;
av_seek_frame(ic, streamIndex, frame * time_base.den / time_base.num / frequency, 0); avformat_seek_file(formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY);
numFrames = 0; avcodec_flush_buffers(codecCtx);
framesPlayed = frame; readNextPacket = YES; // so we immediately read next packet
bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame
return frame; return frame;
} }
@ -304,7 +298,7 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
[NSNumber numberWithBool:floatingPoint], @"floatingPoint", [NSNumber numberWithBool:floatingPoint], @"floatingPoint",
[NSNumber numberWithDouble:totalFrames], @"totalFrames", [NSNumber numberWithDouble:totalFrames], @"totalFrames",
[NSNumber numberWithInt:bitrate], @"bitrate", [NSNumber numberWithInt:bitrate], @"bitrate",
[NSNumber numberWithBool:([source seekable] && seekable)], @"seekable", [NSNumber numberWithBool:seekable], @"seekable",
@"host", @"endian", @"host", @"endian",
nil]; nil];
} }
@ -312,12 +306,12 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op)
+ (NSArray *)fileTypes + (NSArray *)fileTypes
{ {
return [NSArray arrayWithObjects:@"wma", @"asf", @"xwma", @"tak", @"mp3", @"mp2", @"m2a", @"mpa", nil]; return [NSArray arrayWithObjects:@"wma", @"asf", @"xwma", @"tak", @"mp3", @"mp2", @"m2a", @"mpa", @"ape", nil];
} }
+ (NSArray *)mimeTypes + (NSArray *)mimeTypes
{ {
return [NSArray arrayWithObjects:@"application/wma", @"application/x-wma", @"audio/x-wma", @"audio/x-ms-wma", @"audio/x-tak", @"audio/mpeg", @"audio/x-mp3", @"audio/x-mp2", nil]; return [NSArray arrayWithObjects:@"application/wma", @"application/x-wma", @"audio/x-wma", @"audio/x-ms-wma", @"audio/x-tak", @"audio/mpeg", @"audio/x-mp3", @"audio/x-mp2", @"audio/x-ape", nil];
} }