Бравые разработчики инструментариев дополненной реальности Qualcomm Vuforia знают о проблеме снижения частоты кадров потокового видео в ваших AR-приложениях на iOS 8 и уже приготовили решение. По их словам, проблема коренится именно в новой операционной системе Apple и была определена как проблема с частотой, с которой обновление видеотаймера события срабатывает в образце (смотрите переменную frameTimer в классе VideoPlayerHelper).
Вы можете восстановить правильную частоту кадров и работать с плавным видео на всех версиях iOS, выполнив следующие действия.
Класс SimpleTimer
Для начала создайте класс под названием «SimpleTimer», который предполагает простую реализацию таймера с возможностью планировать повторяющиеся действия с заданной скоростью. Вы можете использовать следующий код для части объявления класса (заголовок SimpleTimer.h):
#pragma mark — SimpleTimer
@protocol SimpleTimerDelegate <NSObject>
@required
-(void)timerDidTick;
@end
@interface SimpleTimer : NSObject
{
pthread_t timerThread;
BOOL running;
}
@property (nonatomic, assign) id <SimpleTimerDelegate> delegate;
@property (atomic, readwrite) int exitRequested;
@property (nonatomic, readonly) double interval;
-(id)initWithInterval:(double) interval andDelegate:(id<SimpleTimerDelegate>)delegate;
-(void)start;
-(void)stop;
@end
Затем добавьте следующий код фактического выполнения класса (файл SimpleTimer.m):
#import «SimpleTimer.h»
static void* timerThreadRoutine(void* p)
{
SimpleTimer* self = (SimpleTimer*)p;
useconds_t sleepTime = self.interval * USEC_PER_SEC;
while (!self.exitRequested) {
usleep(sleepTime);
// Create an autorelease pool so the delegate method will not leak
// autoreleased objects
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
[self.delegate timerDidTick];
[pool release];
}
[self release];
return NULL;
}
@implementation SimpleTimer
-(id)initWithInterval:(double) interval andDelegate:(id<SimpleTimerDelegate>)delegate
{
self = [super init];
if (self) {
_interval = interval;
_delegate = delegate;
running = NO;
}
return self;
}
-(void)start
{
if (!running) {
// Ensure we cannot be destroyed while our thread is running
[self retain];
[self setExitRequested:0];
// We will not attempt to join the timer thread, which means the stop
// method can be safely called on it
pthread_attr_t atts;
pthread_attr_init(&atts);
pthread_attr_setdetachstate(&atts, PTHREAD_CREATE_DETACHED);
if (0 == pthread_create(&timerThread, &atts, timerThreadRoutine, self)) {
running = YES;
}
else {
[self release];
}
}
}
-(void)stop
{
if (running) {
[self setExitRequested:1];
running = NO;
}
}
@end
Как только класс SimpleTimer был определён, убедитесь в том, что добавили SimpleTimer.h и SimpleTimer.m к образцу VideoPlayback в Xcode (эти файлы можно добавить в фолдер /VideoPlayback/Classes/apps/VideoPlayback/)
Изменения кода в VideoPlayerHelper.h
Отредактируйте файл VideoPlayerHelper.h (адрес — VideoPlayback/Classes/apps/VideoPlayback/), добавив следующий оператор импорта:
#import «SimpleTimer.h»
Добавьте следующее объявление членов в список @private:
SimpleTimer * simpleFrameTimer;
Кроме того измените объявление @interface в VideoPlayerHelper, добавив протокол <SimpleTimerDelegate>, то есть заменив эту строку:
@interface VideoPlayerHelper : NSObject {
на эту:
@interface VideoPlayerHelper : NSObject <SimpleTimerDelegate> {
Изменения в VideoPlayerHelper.mm
Добавьте этот метод, реализующий протокол SimpleTimerDelegate:
// implement timerDidTick method of <SimpleTimerDelegate> protocol
— (void)timerDidTick
{
[self getNextVideoFrame];
}
И также этот метод:
— (void)stopFrameTimer
{
// Stop the SimpleTimer
[simpleFrameTimer stop];
[simpleFrameTimer release];
simpleFrameTimer = nil;
// Make sure we do not leak a sample buffer
[latestSampleBufferLock lock];
if (NULL != latestSampleBuffer) {
// Release the latest sample buffer
CFRelease(latestSampleBuffer);
latestSampleBuffer = NULL;
}
[latestSampleBufferLock unlock];
}
Далее в методе play:fromPosition: замените это:
[self performSelectorInBackground:@selector(createFrameTimer) withObject:nil];
на это:
[self createFrameTimer];
В методе pause замените это:
[self waitForFrameTimerThreadToEnd];
на это:
[self stopFrameTimer];
В методе stop это:
[self waitForFrameTimerThreadToEnd];
на это:
[self stopFrameTimer];
В методе getNextVideoFrame это:
[frameTimer invalidate];
на это:
[self stopFrameTimer];
Наконец, в методе createFrameTimer замените весь код реализации метода на эти две строки:
simpleFrameTimer = [[SimpleTimer alloc] initWithInterval:(1 / videoFrameRate) andDelegate:self];
[simpleFrameTimer start];
По материалам Vuforia