2008-02-26 20:53:18 +00:00
|
|
|
//
|
2013-10-07 17:33:35 +00:00
|
|
|
// FFMPEGDecoder.m
|
|
|
|
// FFMPEG
|
2008-02-26 20:53:18 +00:00
|
|
|
//
|
|
|
|
// Created by Andre Reffhaug on 2/26/08.
|
|
|
|
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
// test
|
2013-10-07 17:33:35 +00:00
|
|
|
#import "FFMPEGDecoder.h"
|
2008-02-26 20:53:18 +00:00
|
|
|
|
2013-10-02 21:58:18 +00:00
|
|
|
#include <pthread.h>
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
|
|
|
|
2008-02-28 05:33:56 +00:00
|
|
|
#define ST_BUFF 2048
|
2008-02-26 20:53:18 +00:00
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size)
|
|
|
|
{
|
|
|
|
id source = (__bridge id) opaque;
|
|
|
|
return (int) [source read:buf amount:buf_size];
|
|
|
|
}
|
|
|
|
|
|
|
|
int ffmpeg_write(void *opaque, uint8_t *buf, int buf_size)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence)
|
|
|
|
{
|
|
|
|
id source = (__bridge id) opaque;
|
2016-07-17 06:03:44 +00:00
|
|
|
if (whence & AVSEEK_SIZE)
|
|
|
|
{
|
|
|
|
if ([source seekable])
|
|
|
|
{
|
|
|
|
int64_t curOffset = [source tell];
|
|
|
|
[source seek:0 whence:SEEK_END];
|
|
|
|
int64_t size = [source tell];
|
|
|
|
[source seek:curOffset whence:SEEK_SET];
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
whence &= ~(AVSEEK_SIZE | AVSEEK_FORCE);
|
2016-07-16 04:41:23 +00:00
|
|
|
return [source seekable] ? ([source seek:offset whence:whence] ? [source tell] : -1) : -1;
|
2016-07-15 16:26:18 +00:00
|
|
|
}
|
|
|
|
|
2013-10-07 17:33:35 +00:00
|
|
|
@implementation FFMPEGDecoder
|
2008-02-26 20:53:18 +00:00
|
|
|
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2013-10-02 21:58:18 +00:00
|
|
|
int lockmgr_callback(void ** mutex, enum AVLockOp op)
|
|
|
|
{
|
|
|
|
switch (op)
|
|
|
|
{
|
|
|
|
case AV_LOCK_CREATE:
|
|
|
|
*mutex = malloc(sizeof(pthread_mutex_t));
|
|
|
|
pthread_mutex_init(*mutex, NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_LOCK_DESTROY:
|
|
|
|
pthread_mutex_destroy(*mutex);
|
|
|
|
free(*mutex);
|
|
|
|
*mutex = NULL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_LOCK_OBTAIN:
|
|
|
|
pthread_mutex_lock(*mutex);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_LOCK_RELEASE:
|
|
|
|
pthread_mutex_unlock(*mutex);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2013-10-02 21:58:18 +00:00
|
|
|
+ (void)initialize
|
|
|
|
{
|
2013-10-11 03:55:32 +00:00
|
|
|
if(self == [FFMPEGDecoder class])
|
|
|
|
{
|
|
|
|
av_log_set_flags(AV_LOG_SKIP_REPEATED);
|
|
|
|
av_log_set_level(AV_LOG_ERROR);
|
|
|
|
av_register_all();
|
|
|
|
av_lockmgr_register(lockmgr_callback);
|
|
|
|
}
|
2013-10-02 21:58:18 +00:00
|
|
|
}
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2016-06-19 19:57:18 +00:00
|
|
|
- (id)init
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
if (self) {
|
|
|
|
lastReadPacket = NULL;
|
|
|
|
lastDecodedFrame = NULL;
|
|
|
|
codecCtx = NULL;
|
|
|
|
formatCtx = NULL;
|
2016-07-15 16:26:18 +00:00
|
|
|
ioCtx = NULL;
|
|
|
|
buffer = NULL;
|
2016-06-19 19:57:18 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2008-02-28 05:33:56 +00:00
|
|
|
- (BOOL)open:(id<CogSource>)s
|
|
|
|
{
|
2013-10-11 03:55:32 +00:00
|
|
|
int errcode, i;
|
2016-12-04 05:40:53 +00:00
|
|
|
AVStream *stream;
|
2016-07-15 16:26:18 +00:00
|
|
|
|
|
|
|
source = s;
|
2008-02-28 13:11:37 +00:00
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
formatCtx = NULL;
|
|
|
|
totalFrames = 0;
|
|
|
|
framesRead = 0;
|
|
|
|
|
2008-02-28 12:49:19 +00:00
|
|
|
// register all available codecs
|
2016-07-15 16:26:18 +00:00
|
|
|
|
|
|
|
buffer = av_malloc(128 * 1024);
|
|
|
|
if (!buffer)
|
|
|
|
{
|
|
|
|
ALog(@"Out of memory!");
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ioCtx = avio_alloc_context(buffer, 128 * 1024, 0, (__bridge void *)source, ffmpeg_read, ffmpeg_write, ffmpeg_seek);
|
|
|
|
if (!ioCtx)
|
|
|
|
{
|
|
|
|
ALog(@"Unable to create AVIO context");
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
formatCtx = avformat_alloc_context();
|
|
|
|
if (!formatCtx)
|
|
|
|
{
|
|
|
|
ALog(@"Unable to allocate AVFormat context");
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
formatCtx->pb = ioCtx;
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
if ((errcode = avformat_open_input(&formatCtx, "", NULL, NULL)) < 0)
|
2008-03-01 14:11:30 +00:00
|
|
|
{
|
2013-10-11 03:55:32 +00:00
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
2016-07-15 16:26:18 +00:00
|
|
|
ALog(@"Error opening file, errcode = %d, error = %s", errcode, errDescr);
|
2008-03-01 14:11:30 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
if((errcode = avformat_find_stream_info(formatCtx, NULL)) < 0)
|
2013-10-11 03:55:32 +00:00
|
|
|
{
|
2016-07-15 16:26:18 +00:00
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
|
|
|
ALog(@"Can't find stream info, errcode = %d, error = %s", errcode, errDescr);
|
2013-10-11 03:55:32 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2013-10-03 10:44:39 +00:00
|
|
|
streamIndex = -1;
|
2016-07-15 16:26:18 +00:00
|
|
|
AVCodecParameters *codecPar;
|
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
for(i = 0; i < formatCtx->nb_streams; i++) {
|
2016-12-04 05:40:53 +00:00
|
|
|
stream = formatCtx->streams[i];
|
|
|
|
codecPar = stream->codecpar;
|
|
|
|
if(streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO)
|
2008-02-28 05:33:56 +00:00
|
|
|
{
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"audio codec found");
|
2013-10-03 10:44:39 +00:00
|
|
|
streamIndex = i;
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
2016-12-04 05:40:53 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
stream->discard = AVDISCARD_ALL;
|
|
|
|
}
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
2013-10-03 10:44:39 +00:00
|
|
|
|
|
|
|
if ( streamIndex < 0 ) {
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"no audio codec found");
|
2013-10-03 10:44:39 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2016-07-15 16:26:18 +00:00
|
|
|
|
2016-12-04 05:40:53 +00:00
|
|
|
stream = formatCtx->streams[streamIndex];
|
2017-05-21 01:29:27 +00:00
|
|
|
codecPar = stream->codecpar;
|
2016-12-04 05:40:53 +00:00
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
codecCtx = avcodec_alloc_context3(NULL);
|
|
|
|
if (!codecCtx)
|
|
|
|
{
|
|
|
|
ALog(@"could not allocate codec context");
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (errcode = avcodec_parameters_to_context(codecCtx, codecPar)) < 0 )
|
|
|
|
{
|
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
|
|
|
ALog(@"Can't copy codec parameters to context, errcode = %d, error = %s", errcode, errDescr);
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2016-12-04 05:40:53 +00:00
|
|
|
av_codec_set_pkt_timebase(codecCtx, stream->time_base);
|
2008-02-28 05:33:56 +00:00
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
AVCodec * codec = avcodec_find_decoder(codecCtx->codec_id);
|
2008-02-28 05:33:56 +00:00
|
|
|
if (!codec) {
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"codec not found");
|
2008-02-28 05:33:56 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
if ( (errcode = avcodec_open2(codecCtx, codec, NULL)) < 0) {
|
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
|
|
|
ALog(@"could not open codec, errcode = %d, error = %s", errcode, errDescr);
|
2008-02-28 05:33:56 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2014-03-10 04:16:37 +00:00
|
|
|
lastDecodedFrame = av_frame_alloc();
|
|
|
|
av_frame_unref(lastDecodedFrame);
|
2013-10-11 03:55:32 +00:00
|
|
|
lastReadPacket = malloc(sizeof(AVPacket));
|
|
|
|
av_new_packet(lastReadPacket, 0);
|
|
|
|
readNextPacket = YES;
|
2013-10-11 04:52:32 +00:00
|
|
|
bytesConsumedFromDecodedFrame = INT_MAX;
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
frequency = codecCtx->sample_rate;
|
|
|
|
channels = codecCtx->channels;
|
2013-10-05 21:15:09 +00:00
|
|
|
floatingPoint = NO;
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
switch (codecCtx->sample_fmt) {
|
2013-10-02 21:58:18 +00:00
|
|
|
case AV_SAMPLE_FMT_U8:
|
|
|
|
case AV_SAMPLE_FMT_U8P:
|
|
|
|
bitsPerSample = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_S16:
|
|
|
|
case AV_SAMPLE_FMT_S16P:
|
|
|
|
bitsPerSample = 16;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case AV_SAMPLE_FMT_S32:
|
|
|
|
case AV_SAMPLE_FMT_S32P:
|
2013-10-05 21:15:09 +00:00
|
|
|
bitsPerSample = 32;
|
|
|
|
break;
|
|
|
|
|
2013-10-02 21:58:18 +00:00
|
|
|
case AV_SAMPLE_FMT_FLT:
|
|
|
|
case AV_SAMPLE_FMT_FLTP:
|
2013-10-05 21:15:09 +00:00
|
|
|
bitsPerSample = 32;
|
|
|
|
floatingPoint = YES;
|
|
|
|
break;
|
|
|
|
|
2013-10-02 21:58:18 +00:00
|
|
|
case AV_SAMPLE_FMT_DBL:
|
|
|
|
case AV_SAMPLE_FMT_DBLP:
|
2013-10-05 21:15:09 +00:00
|
|
|
bitsPerSample = 64;
|
|
|
|
floatingPoint = YES;
|
2013-10-02 21:58:18 +00:00
|
|
|
break;
|
2013-10-03 08:00:58 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
return NO;
|
2013-10-02 21:58:18 +00:00
|
|
|
}
|
2013-10-11 03:55:32 +00:00
|
|
|
|
2016-07-16 04:41:23 +00:00
|
|
|
//totalFrames = codecCtx->sample_rate * ((float)formatCtx->duration/AV_TIME_BASE);
|
|
|
|
AVRational tb = (AVRational) { 1, codecCtx->sample_rate };
|
2016-12-04 05:40:53 +00:00
|
|
|
totalFrames = av_rescale_q(stream->duration, stream->time_base, tb);
|
2016-07-15 16:26:18 +00:00
|
|
|
bitrate = (int)((codecCtx->bit_rate) / 1000);
|
2013-10-11 03:55:32 +00:00
|
|
|
framesRead = 0;
|
2014-04-26 22:38:45 +00:00
|
|
|
endOfStream = NO;
|
2016-07-16 04:41:23 +00:00
|
|
|
endOfAudio = NO;
|
2013-10-11 03:55:32 +00:00
|
|
|
|
2014-07-04 08:07:55 +00:00
|
|
|
if ( totalFrames < 0 )
|
|
|
|
totalFrames = 0;
|
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
seekable = [s seekable];
|
2008-02-28 12:49:19 +00:00
|
|
|
|
2008-02-28 05:33:56 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)close
|
|
|
|
{
|
2013-10-11 03:55:32 +00:00
|
|
|
if (lastReadPacket)
|
|
|
|
{
|
2016-05-07 06:35:04 +00:00
|
|
|
av_packet_unref(lastReadPacket);
|
2013-10-11 03:55:32 +00:00
|
|
|
free(lastReadPacket);
|
|
|
|
lastReadPacket = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastDecodedFrame) { av_free(lastDecodedFrame); lastDecodedFrame = NULL; }
|
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
if (codecCtx) { avcodec_close(codecCtx); avcodec_free_context(&codecCtx); codecCtx = NULL; }
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
if (formatCtx) { avformat_close_input(&(formatCtx)); formatCtx = NULL; }
|
2016-07-15 16:26:18 +00:00
|
|
|
|
2016-07-16 04:41:23 +00:00
|
|
|
if (ioCtx) { buffer = ioCtx->buffer; av_free(ioCtx); ioCtx = NULL; }
|
2016-07-15 16:26:18 +00:00
|
|
|
|
|
|
|
if (buffer) { av_free(buffer); buffer = NULL; }
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
|
|
|
|
2016-06-19 19:57:18 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
|
2008-02-28 05:33:56 +00:00
|
|
|
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
|
|
|
{
|
2014-07-04 08:07:55 +00:00
|
|
|
if ( totalFrames && framesRead >= totalFrames )
|
2014-03-13 03:39:01 +00:00
|
|
|
return 0;
|
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
int frameSize = channels * (bitsPerSample / 8);
|
|
|
|
int dataSize = 0;
|
|
|
|
|
|
|
|
int bytesToRead = frames * frameSize;
|
|
|
|
int bytesRead = 0;
|
|
|
|
|
2016-07-15 16:26:18 +00:00
|
|
|
int errcode;
|
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
int8_t* targetBuf = (int8_t*) buf;
|
|
|
|
memset(buf, 0, bytesToRead);
|
|
|
|
|
|
|
|
while (bytesRead < bytesToRead)
|
|
|
|
{
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
// buffer size needed to hold decoded samples, in bytes
|
|
|
|
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);
|
|
|
|
|
2014-03-10 04:16:37 +00:00
|
|
|
if ( dataSize < 0 )
|
|
|
|
dataSize = 0;
|
|
|
|
|
2016-07-16 04:41:23 +00:00
|
|
|
while(readNextPacket && !endOfAudio)
|
2013-10-11 03:55:32 +00:00
|
|
|
{
|
|
|
|
// consume next chunk of encoded data from input stream
|
2016-07-16 04:41:23 +00:00
|
|
|
if (!endOfStream)
|
2013-10-09 06:57:58 +00:00
|
|
|
{
|
2016-07-16 04:41:23 +00:00
|
|
|
av_packet_unref(lastReadPacket);
|
|
|
|
if((errcode = av_read_frame(formatCtx, lastReadPacket)) < 0)
|
2016-07-15 16:26:18 +00:00
|
|
|
{
|
2016-07-16 04:41:23 +00:00
|
|
|
if (errcode == AVERROR_EOF)
|
|
|
|
{
|
|
|
|
DLog(@"End of stream");
|
|
|
|
endOfStream = YES;
|
|
|
|
}
|
|
|
|
if (formatCtx->pb && formatCtx->pb->error) break;
|
2016-07-15 16:26:18 +00:00
|
|
|
}
|
2016-07-16 04:41:23 +00:00
|
|
|
if (lastReadPacket->stream_index != streamIndex)
|
|
|
|
continue;
|
2013-10-09 06:57:58 +00:00
|
|
|
}
|
|
|
|
|
2016-07-16 04:41:23 +00:00
|
|
|
if ((errcode = avcodec_send_packet(codecCtx, endOfStream ? NULL : lastReadPacket)) < 0)
|
|
|
|
{
|
|
|
|
if (errcode != AVERROR(EAGAIN))
|
|
|
|
{
|
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
|
|
|
ALog(@"Error sending packet to codec, errcode = %d, error = %s", errcode, errDescr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2014-03-10 04:16:37 +00:00
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
readNextPacket = NO; // we probably won't need to consume another chunk
|
|
|
|
}
|
2016-07-16 04:41:23 +00:00
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
if (dataSize <= bytesConsumedFromDecodedFrame)
|
|
|
|
{
|
2016-07-16 04:41:23 +00:00
|
|
|
if (endOfStream && endOfAudio)
|
2013-10-11 12:03:55 +00:00
|
|
|
break;
|
|
|
|
|
2013-10-11 03:55:32 +00:00
|
|
|
bytesConsumedFromDecodedFrame = 0;
|
2016-07-16 04:41:23 +00:00
|
|
|
|
|
|
|
if ((errcode = avcodec_receive_frame(codecCtx, lastDecodedFrame)) < 0)
|
|
|
|
{
|
|
|
|
if (errcode == AVERROR_EOF)
|
2014-03-10 04:16:37 +00:00
|
|
|
{
|
2016-07-16 04:41:23 +00:00
|
|
|
endOfAudio = YES;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (errcode == AVERROR(EAGAIN))
|
|
|
|
{
|
|
|
|
// Read another packet
|
|
|
|
readNextPacket = YES;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char errDescr[4096];
|
|
|
|
av_strerror(errcode, errDescr, 4096);
|
|
|
|
ALog(@"Error receiving frame, errcode = %d, error = %s", errcode, errDescr);
|
|
|
|
return 0;
|
2014-03-10 04:16:37 +00:00
|
|
|
}
|
2013-10-02 21:58:18 +00:00
|
|
|
}
|
2016-07-16 04:41:23 +00:00
|
|
|
|
|
|
|
// Something has been successfully decoded
|
|
|
|
dataSize = av_samples_get_buffer_size(&planeSize, codecCtx->channels,
|
|
|
|
lastDecodedFrame->nb_samples,
|
|
|
|
codecCtx->sample_fmt, 1);
|
2014-03-10 04:16:37 +00:00
|
|
|
|
2016-07-16 04:41:23 +00:00
|
|
|
if ( dataSize < 0 )
|
|
|
|
dataSize = 0;
|
2013-10-02 21:58:18 +00:00
|
|
|
}
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
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;
|
2013-10-11 04:52:32 +00:00
|
|
|
int bytesConsumedPerPlane = bytesConsumedFromDecodedFrame / channels;
|
2013-10-11 03:55:32 +00:00
|
|
|
int toConsumePerPlane = toConsume / channels;
|
|
|
|
for (int s = 0; s < toConsumePerPlane; s += bytesPerSample) {
|
|
|
|
for (int ch = 0; ch < channels; ++ch) {
|
|
|
|
memcpy(out, lastDecodedFrame->extended_data[ch] + bytesConsumedPerPlane + s, bytesPerSample);
|
|
|
|
out += bytesPerSample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bytesConsumedFromDecodedFrame += toConsume;
|
|
|
|
bytesRead += toConsume;
|
|
|
|
}
|
|
|
|
|
|
|
|
int framesReadNow = bytesRead / frameSize;
|
2014-07-04 08:07:55 +00:00
|
|
|
if ( totalFrames && ( framesRead + framesReadNow > totalFrames ) )
|
2016-07-15 16:26:18 +00:00
|
|
|
framesReadNow = (int)(totalFrames - framesRead);
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
framesRead += framesReadNow;
|
|
|
|
|
|
|
|
return framesReadNow;
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (long)seek:(long)frame
|
|
|
|
{
|
2014-07-04 08:07:55 +00:00
|
|
|
if ( !totalFrames )
|
|
|
|
return -1;
|
|
|
|
|
2014-03-26 09:27:51 +00:00
|
|
|
if (frame >= totalFrames)
|
|
|
|
{
|
|
|
|
framesRead = totalFrames;
|
2014-04-26 22:38:45 +00:00
|
|
|
endOfStream = YES;
|
2016-07-16 04:41:23 +00:00
|
|
|
endOfAudio = YES;
|
2014-03-26 09:27:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2013-10-11 03:55:32 +00:00
|
|
|
int64_t ts = frame * (formatCtx->duration) / totalFrames;
|
|
|
|
avformat_seek_file(formatCtx, -1, ts - 1000, ts, ts, AVSEEK_FLAG_ANY);
|
|
|
|
avcodec_flush_buffers(codecCtx);
|
|
|
|
readNextPacket = YES; // so we immediately read next packet
|
|
|
|
bytesConsumedFromDecodedFrame = INT_MAX; // so we immediately begin decoding next frame
|
2013-11-15 07:28:02 +00:00
|
|
|
framesRead = frame;
|
2014-04-26 22:38:45 +00:00
|
|
|
endOfStream = NO;
|
2016-07-16 04:41:23 +00:00
|
|
|
endOfAudio = NO;
|
2013-10-11 03:55:32 +00:00
|
|
|
|
|
|
|
return frame;
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
- (NSDictionary *)properties
|
|
|
|
{
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt:channels], @"channels",
|
|
|
|
[NSNumber numberWithInt:bitsPerSample], @"bitsPerSample",
|
2013-10-07 18:26:23 +00:00
|
|
|
[NSNumber numberWithBool:(bitsPerSample == 8)], @"Unsigned",
|
2008-02-28 05:33:56 +00:00
|
|
|
[NSNumber numberWithFloat:frequency], @"sampleRate",
|
2013-10-05 21:15:09 +00:00
|
|
|
[NSNumber numberWithBool:floatingPoint], @"floatingPoint",
|
2008-02-28 05:33:56 +00:00
|
|
|
[NSNumber numberWithDouble:totalFrames], @"totalFrames",
|
|
|
|
[NSNumber numberWithInt:bitrate], @"bitrate",
|
2013-10-11 03:55:32 +00:00
|
|
|
[NSNumber numberWithBool:seekable], @"seekable",
|
2013-10-05 21:15:09 +00:00
|
|
|
@"host", @"endian",
|
2008-02-28 05:33:56 +00:00
|
|
|
nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+ (NSArray *)fileTypes
|
|
|
|
{
|
2017-12-17 03:58:49 +00:00
|
|
|
return [NSArray arrayWithObjects:@"wma", @"asf", @"tak", @"mp3", @"mp2", @"m2a", @"mpa", @"ape", @"ac3", @"dts", @"dtshd", @"wav", @"tta", @"vqf", @"vqe", @"vql", @"ra", @"rm", @"rmj", nil];
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray *)mimeTypes
|
|
|
|
{
|
2014-02-06 01:49:56 +00:00
|
|
|
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", @"audio/x-ac3", @"audio/x-dts", @"audio/x-dtshd", @"audio/x-at3", @"audio/wav", @"audio/tta", @"audio/x-tta", @"audio/x-twinvq", nil];
|
2008-02-28 05:33:56 +00:00
|
|
|
}
|
|
|
|
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
+ (float)priority
|
|
|
|
{
|
|
|
|
return 1.0;
|
|
|
|
}
|
2008-02-28 05:33:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-02-26 20:53:18 +00:00
|
|
|
@end
|