diff options
Diffstat (limited to 'drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm')
-rw-r--r-- | drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm | 457 |
1 files changed, 0 insertions, 457 deletions
diff --git a/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm b/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm deleted file mode 100644 index 1b5cf0ab13..0000000000 --- a/drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm +++ /dev/null @@ -1,457 +0,0 @@ -/************************************************************************************ -This source file is part of the Theora Video Playback Library -For latest info, see http://libtheoraplayer.googlecode.com -************************************************************************************* -Copyright (c) 2008-2014 Kresimir Spes (kspes@cateia.com) -This program is free software; you can redistribute it and/or modify it under -the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause -*************************************************************************************/ -#ifdef __AVFOUNDATION -#define AVFOUNDATION_CLASSES_DEFINED -#import <AVFoundation/AVFoundation.h> -#include "TheoraAudioInterface.h" -#include "TheoraDataSource.h" -#include "TheoraException.h" -#include "TheoraTimer.h" -#include "TheoraUtil.h" -#include "TheoraFrameQueue.h" -#include "TheoraVideoFrame.h" -#include "TheoraVideoManager.h" -#include "TheoraVideoClip_AVFoundation.h" -#include "TheoraPixelTransform.h" - -#ifdef _AVFOUNDATION_BGRX -// a fast function developed to use kernel byte swapping calls to optimize alpha decoding. -// In AVFoundation, BGRX mode conversion is prefered to YUV conversion because apple's YUV -// conversion on iOS seems to run faster than libtheoraplayer's implementation -// This may change in the future with more optimizations to libtheoraplayers's YUV conversion -// code, making this function obsolete. -static void bgrx2rgba(unsigned char* dest, int w, int h, struct TheoraPixelTransform* t) -{ - unsigned register int a; - unsigned int *dst = (unsigned int*) dest, *dstEnd; - unsigned char* src = t->raw; - int y, x, ax; - - for (y = 0; y < h; ++y, src += t->rawStride) - { - for (x = 0, ax = w * 4, dstEnd = dst + w; dst != dstEnd; x += 4, ax += 4, ++dst) - { - // use the full alpha range here because the Y channel has already been converted - // to RGB and that's in [0, 255] range. - a = src[ax]; - *dst = (OSReadSwapInt32(src, x) >> 8) | (a << 24); - } - } -} -#endif - -static CVPlanarPixelBufferInfo_YCbCrPlanar getYUVStruct(void* src) -{ - CVPlanarPixelBufferInfo_YCbCrPlanar* bigEndianYuv = (CVPlanarPixelBufferInfo_YCbCrPlanar*) src; - CVPlanarPixelBufferInfo_YCbCrPlanar yuv; - yuv.componentInfoY.offset = OSSwapInt32(bigEndianYuv->componentInfoY.offset); - yuv.componentInfoY.rowBytes = OSSwapInt32(bigEndianYuv->componentInfoY.rowBytes); - yuv.componentInfoCb.offset = OSSwapInt32(bigEndianYuv->componentInfoCb.offset); - yuv.componentInfoCb.rowBytes = OSSwapInt32(bigEndianYuv->componentInfoCb.rowBytes); - yuv.componentInfoCr.offset = OSSwapInt32(bigEndianYuv->componentInfoCr.offset); - yuv.componentInfoCr.rowBytes = OSSwapInt32(bigEndianYuv->componentInfoCr.rowBytes); - return yuv; -} - -TheoraVideoClip_AVFoundation::TheoraVideoClip_AVFoundation(TheoraDataSource* data_source, - TheoraOutputMode output_mode, - int nPrecachedFrames, - bool usePower2Stride): - TheoraVideoClip(data_source, output_mode, nPrecachedFrames, usePower2Stride), - TheoraAudioPacketQueue() -{ - mLoaded = 0; - mReader = NULL; - mOutput = mAudioOutput = NULL; - mReadAudioSamples = mAudioFrequency = mNumAudioChannels = 0; -} - -TheoraVideoClip_AVFoundation::~TheoraVideoClip_AVFoundation() -{ - unload(); -} - -void TheoraVideoClip_AVFoundation::unload() -{ - if (mOutput != NULL || mAudioOutput != NULL || mReader != NULL) - { - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - if (mOutput != NULL) - { - [mOutput release]; - mOutput = NULL; - } - - if (mAudioOutput) - { - [mAudioOutput release]; - mAudioOutput = NULL; - } - - if (mReader != NULL) - { - [mReader release]; - mReader = NULL; - } - - [pool release]; - } -} - -bool TheoraVideoClip_AVFoundation::_readData() -{ - return 1; -} - -bool TheoraVideoClip_AVFoundation::decodeNextFrame() -{ - if (mReader == NULL || mEndOfFile) return 0; - AVAssetReaderStatus status = [mReader status]; - if (status == AVAssetReaderStatusFailed) - { - // This can happen on iOS when you suspend the app... Only happens on the device, iOS simulator seems to work fine. - th_writelog("AVAssetReader reading failed, restarting..."); - - mSeekFrame = mTimer->getTime() * mFPS; - // just in case - if (mSeekFrame < 0) mSeekFrame = 0; - if (mSeekFrame > mDuration * mFPS - 1) mSeekFrame = mDuration * mFPS - 1; - _restart(); - status = [mReader status]; - if (status == AVAssetReaderStatusFailed) - { - th_writelog("AVAssetReader restart failed!"); - return 0; - } - th_writelog("AVAssetReader restart succeeded!"); - } - - TheoraVideoFrame* frame = mFrameQueue->requestEmptyFrame(); - if (!frame) return 0; - - CMSampleBufferRef sampleBuffer = NULL; - NSAutoreleasePool* pool = NULL; - CMTime presentationTime; - - if (mAudioInterface) decodeAudio(); - - if (status == AVAssetReaderStatusReading) - { - pool = [[NSAutoreleasePool alloc] init]; - - while ((sampleBuffer = [mOutput copyNextSampleBuffer])) - { - presentationTime = CMSampleBufferGetOutputPresentationTimeStamp(sampleBuffer); - frame->mTimeToDisplay = (float) CMTimeGetSeconds(presentationTime); - frame->mIteration = mIteration; - frame->_setFrameNumber(mFrameNumber); - ++mFrameNumber; - if (frame->mTimeToDisplay < mTimer->getTime() && !mRestarted && mFrameNumber % 16 != 0) - { - // %16 operation is here to prevent a playback halt during video playback if the decoder can't keep up with demand. -#ifdef _DEBUG - th_writelog(mName + ": pre-dropped frame " + str(mFrameNumber - 1)); -#endif - ++mNumDisplayedFrames; - ++mNumDroppedFrames; - CMSampleBufferInvalidate(sampleBuffer); - CFRelease(sampleBuffer); - sampleBuffer = NULL; - continue; // drop frame - } - - CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferLockBaseAddress(imageBuffer, 0); - void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); - - mStride = CVPixelBufferGetBytesPerRow(imageBuffer); - size_t width = CVPixelBufferGetWidth(imageBuffer); - size_t height = CVPixelBufferGetHeight(imageBuffer); - - TheoraPixelTransform t; - memset(&t, 0, sizeof(TheoraPixelTransform)); -#ifdef _AVFOUNDATION_BGRX - if (mOutputMode == TH_BGRX || mOutputMode == TH_RGBA) - { - t.raw = (unsigned char*) baseAddress; - t.rawStride = mStride; - } - else -#endif - { - CVPlanarPixelBufferInfo_YCbCrPlanar yuv = getYUVStruct(baseAddress); - - t.y = (unsigned char*) baseAddress + yuv.componentInfoY.offset; t.yStride = yuv.componentInfoY.rowBytes; - t.u = (unsigned char*) baseAddress + yuv.componentInfoCb.offset; t.uStride = yuv.componentInfoCb.rowBytes; - t.v = (unsigned char*) baseAddress + yuv.componentInfoCr.offset; t.vStride = yuv.componentInfoCr.rowBytes; - } -#ifdef _AVFOUNDATION_BGRX - if (mOutputMode == TH_RGBA) - { - for (int i = 0; i < 1000; ++i) - bgrx2rgba(frame->getBuffer(), mWidth / 2, mHeight, &t); - frame->mReady = true; - } - else -#endif - frame->decode(&t); - - CVPixelBufferUnlockBaseAddress(imageBuffer, 0); - CMSampleBufferInvalidate(sampleBuffer); - CFRelease(sampleBuffer); - - break; // TODO - should this really be a while loop instead of an if block? - } - } - if (pool) [pool release]; - - if (!frame->mReady) // in case the frame wasn't used - { - frame->mInUse = 0; - } - - if (sampleBuffer == NULL && mReader.status == AVAssetReaderStatusCompleted) // other cases could be app suspended - { - if (mAutoRestart) - { - ++mIteration; - _restart(); - } - else - { - unload(); - mEndOfFile = true; - } - return 0; - } - - - return 1; -} - -void TheoraVideoClip_AVFoundation::_restart() -{ - mEndOfFile = false; - unload(); - load(mStream); - mRestarted = true; -} - -void TheoraVideoClip_AVFoundation::load(TheoraDataSource* source) -{ - mStream = source; - mFrameNumber = 0; - mEndOfFile = false; - TheoraFileDataSource* fileDataSource = dynamic_cast<TheoraFileDataSource*>(source); - std::string filename; - if (fileDataSource != NULL) filename = fileDataSource->getFilename(); - else - { - TheoraMemoryFileDataSource* memoryDataSource = dynamic_cast<TheoraMemoryFileDataSource*>(source); - if (memoryDataSource != NULL) filename = memoryDataSource->getFilename(); - else throw TheoraGenericException("Unable to load MP4 file"); - } - - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - NSString* path = [NSString stringWithUTF8String:filename.c_str()]; - NSError* err; - NSURL *url = [NSURL fileURLWithPath:path]; - AVAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil]; - mReader = [[AVAssetReader alloc] initWithAsset:asset error:&err]; - NSArray* tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; - if ([tracks count] == 0) - throw TheoraGenericException("Unable to open video file: " + filename); - AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; - - NSArray* audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio]; - if (audio_track >= audioTracks.count) - audio_track = 0; - AVAssetTrack *audioTrack = audioTracks.count > 0 ? [audioTracks objectAtIndex:audio_track] : NULL; - printf("*********** using audio track %i\n", audio_track); - -#ifdef _AVFOUNDATION_BGRX - bool yuv_output = (mOutputMode != TH_BGRX && mOutputMode != TH_RGBA); -#else - bool yuv_output = true; -#endif - - NSDictionary *videoOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:(yuv_output) ? kCVPixelFormatType_420YpCbCr8Planar : kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil]; - - mOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoOptions]; - [mReader addOutput:mOutput]; - if ([mOutput respondsToSelector:@selector(setAlwaysCopiesSampleData:)]) // Not supported on iOS versions older than 5.0 - mOutput.alwaysCopiesSampleData = NO; - - mFPS = videoTrack.nominalFrameRate; - mWidth = mSubFrameWidth = mStride = videoTrack.naturalSize.width; - mHeight = mSubFrameHeight = videoTrack.naturalSize.height; - mFrameDuration = 1.0f / mFPS; - mDuration = (float) CMTimeGetSeconds(asset.duration); - if (mFrameQueue == NULL) - { - mFrameQueue = new TheoraFrameQueue(this); - mFrameQueue->setSize(mNumPrecachedFrames); - } - - if (mSeekFrame != -1) - { - mFrameNumber = mSeekFrame; - [mReader setTimeRange: CMTimeRangeMake(CMTimeMakeWithSeconds(mSeekFrame / mFPS, 1), kCMTimePositiveInfinity)]; - } - if (audioTrack) - { - TheoraAudioInterfaceFactory* audio_factory = TheoraVideoManager::getSingleton().getAudioInterfaceFactory(); - if (audio_factory) - { - NSDictionary *audioOptions = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, - [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved, - [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, - [NSNumber numberWithBool:YES], AVLinearPCMIsFloatKey, - [NSNumber numberWithInt:32], AVLinearPCMBitDepthKey, - nil]; - - mAudioOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:audioTrack outputSettings:audioOptions]; - [mReader addOutput:mAudioOutput]; - if ([mAudioOutput respondsToSelector:@selector(setAlwaysCopiesSampleData:)]) // Not supported on iOS versions older than 5.0 - mAudioOutput.alwaysCopiesSampleData = NO; - - NSArray* desclst = audioTrack.formatDescriptions; - CMAudioFormatDescriptionRef desc = (CMAudioFormatDescriptionRef) [desclst objectAtIndex:0]; - const AudioStreamBasicDescription* audioDesc = CMAudioFormatDescriptionGetStreamBasicDescription(desc); - mAudioFrequency = (unsigned int) audioDesc->mSampleRate; - mNumAudioChannels = audioDesc->mChannelsPerFrame; - - if (mSeekFrame != -1) - { - mReadAudioSamples = mFrameNumber * (mAudioFrequency * mNumAudioChannels) / mFPS; - } - else mReadAudioSamples = 0; - - if (mAudioInterface == NULL) - setAudioInterface(audio_factory->createInstance(this, mNumAudioChannels, mAudioFrequency)); - } - } - -#ifdef _DEBUG - else if (!mLoaded) - { - th_writelog("-----\nwidth: " + str(mWidth) + ", height: " + str(mHeight) + ", fps: " + str((int) getFPS())); - th_writelog("duration: " + strf(mDuration) + " seconds\n-----"); - } -#endif - [mReader startReading]; - [pool release]; - mLoaded = true; -} - -void TheoraVideoClip_AVFoundation::decodedAudioCheck() -{ - if (!mAudioInterface || mTimer->isPaused()) return; - - mAudioMutex->lock(); - flushAudioPackets(mAudioInterface); - mAudioMutex->unlock(); -} - -float TheoraVideoClip_AVFoundation::decodeAudio() -{ - if (mRestarted) return -1; - - if (mReader == NULL || mEndOfFile) return 0; - AVAssetReaderStatus status = [mReader status]; - - if (mAudioOutput) - { - CMSampleBufferRef sampleBuffer = NULL; - NSAutoreleasePool* pool = NULL; - bool mutexLocked = 0; - - float factor = 1.0f / (mAudioFrequency * mNumAudioChannels); - float videoTime = (float) mFrameNumber / mFPS; - float min = mFrameQueue->getSize() / mFPS + 1.0f; - - if (status == AVAssetReaderStatusReading) - { - pool = [[NSAutoreleasePool alloc] init]; - - // always buffer up of audio ahead of the frames - while (mReadAudioSamples * factor - videoTime < min) - { - if ((sampleBuffer = [mAudioOutput copyNextSampleBuffer])) - { - AudioBufferList audioBufferList; - - CMBlockBufferRef blockBuffer = NULL; - CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(sampleBuffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, 0, &blockBuffer); - - for (int y = 0; y < audioBufferList.mNumberBuffers; ++y) - { - AudioBuffer audioBuffer = audioBufferList.mBuffers[y]; - float *frame = (float*) audioBuffer.mData; - - if (!mutexLocked) - { - mAudioMutex->lock(); - mutexLocked = 1; - } - addAudioPacket(frame, audioBuffer.mDataByteSize / (mNumAudioChannels * sizeof(float)), mAudioGain); - - mReadAudioSamples += audioBuffer.mDataByteSize / (sizeof(float)); - } - - CFRelease(blockBuffer); - CMSampleBufferInvalidate(sampleBuffer); - CFRelease(sampleBuffer); - } - else - { - [mAudioOutput release]; - mAudioOutput = nil; - break; - } - } - [pool release]; - } - if (mutexLocked) mAudioMutex->unlock(); - } - - return -1; -} - -void TheoraVideoClip_AVFoundation::doSeek() -{ -#if _DEBUG - th_writelog(mName + " [seek]: seeking to frame " + str(mSeekFrame)); -#endif - int frame; - float time = mSeekFrame / getFPS(); - mTimer->seek(time); - bool paused = mTimer->isPaused(); - if (!paused) mTimer->pause(); // pause until seeking is done - - mEndOfFile = false; - mRestarted = false; - - resetFrameQueue(); - unload(); - load(mStream); - - if (mAudioInterface) - { - mAudioMutex->lock(); - destroyAllAudioPackets(); - mAudioMutex->unlock(); - } - - if (!paused) mTimer->play(); - mSeekFrame = -1; -} -#endif |