IOSurfaces - Артефакты в видео и неспособные захватить видео поверхности

Это вопрос из двух частей. У меня работает следующий код, который захватывает текущую поверхность дисплея и создает видеоизображение с поверхностей (все происходит в фоновом режиме).

for(int i=0;i<100;i++){
        IOMobileFramebufferConnection connect;
        kern_return_t result;
        IOSurfaceRef screenSurface = NULL;

        io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleH1CLCD"));
        if(!framebufferService)
            framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleM2CLCD"));
        if(!framebufferService)
            framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleCLCD"));

        result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);

        result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface);

        uint32_t aseed;
        IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
        uint32_t width = IOSurfaceGetWidth(screenSurface);
        uint32_t height = IOSurfaceGetHeight(screenSurface);
        m_width = width;
        m_height = height;
        CFMutableDictionaryRef dict;
        int pitch = width*4, size = width*height*4;
        int bPE=4;
        char pixelFormat[4] = {'A','R','G','B'};
        dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
        CFDictionarySetValue(dict, kIOSurfaceIsGlobal, kCFBooleanTrue);
        CFDictionarySetValue(dict, kIOSurfaceBytesPerRow, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch));
        CFDictionarySetValue(dict, kIOSurfaceBytesPerElement, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bPE));
        CFDictionarySetValue(dict, kIOSurfaceWidth, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width));
        CFDictionarySetValue(dict, kIOSurfaceHeight, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height));
        CFDictionarySetValue(dict, kIOSurfacePixelFormat, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, pixelFormat));
        CFDictionarySetValue(dict, kIOSurfaceAllocSize, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &size));

        IOSurfaceRef destSurf = IOSurfaceCreate(dict);

        IOSurfaceAcceleratorRef outAcc;
        IOSurfaceAcceleratorCreate(NULL, 0, &outAcc);

        IOSurfaceAcceleratorTransferSurface(outAcc, screenSurface, destSurf, dict, NULL);

        IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
        CFRelease(outAcc);

        // MOST RELEVANT PART OF CODE

        CVPixelBufferCreateWithBytes(NULL, width, height, kCVPixelFormatType_32BGRA, IOSurfaceGetBaseAddress(destSurf), IOSurfaceGetBytesPerRow(destSurf), NULL, NULL, NULL, &sampleBuffer);

        CMTime frameTime = CMTimeMake(frameCount, (int32_t)5);

        [adaptor appendPixelBuffer:sampleBuffer withPresentationTime:frameTime];

        CFRelease(sampleBuffer);
        CFRelease(destSurf);
        frameCount++;
    }

P.S: Последние 4-5 строк кода являются наиболее релевантными (если вам нужно отфильтровать).

1) Созданное видео имеет артефакты. Раньше я работал над видео и раньше сталкивался с такой проблемой. Полагаю, для этого могут быть две причины:
я. PixelBuffer, который передается адаптеру, изменяется или освобождается до завершения обработки (кодирование + запись). Это может быть связано с асинхронными вызовами. Но я не уверен, что это сама проблема и как ее решить.
II. Промежуточные метки времени являются неточными (например, 2 кадра, имеющих одну и ту же метку времени, или фрейм, имеющий более низкую временную метку, чем предыдущий кадр). Я выписал значения timestamp, и это не похоже на проблему.

2) Приведенный выше код не может захватить поверхности, когда воспроизводится видео или когда мы играем в игры. Все, что я получаю, - это пустой экран на выходе. Это может быть связано с аппаратным ускоренным декодированием, которое происходит в таких случаях.

Любые входы по любой из двух частей вопросов будут действительно полезными. Кроме того, если у вас есть хорошие ссылки для чтения на IOSurfaces в целом, пожалуйста, разместите их здесь.

Ответ 1

Я немного экспериментировал и пришел к выводу, что поверхность экрана, с которой копируется контент, меняется даже до завершения передачи содержимого (вызов IOSurfaceAcceleratorTransferSurface()). Я использую блокировку (пробовал как асинхронную, так и только для чтения), но она переопределяется iOS. Я изменил код между блокировкой/разблокировкой до минимального:

IOSurfaceLock(screenSurface, kIOSurfaceLockReadOnly, &aseed);
aseed1 = IOSurfaceGetSeed(screenSurface);
IOSurfaceAcceleratorTransferSurface(outAcc, screenSurface, destSurf, dict, NULL);
aseed2 = IOSurfaceGetSeed(screenSurface);
IOSurfaceUnlock(screenSurface, kIOSurfaceLockReadOnly, &aseed);

Функция GetSeed сообщает, изменилось ли содержимое поверхности. И я зарегистрировал счет, указывающий количество кадров, для которых изменяется семя. Счетчик был отличным от нуля. Итак, следующий код разрешил проблему:

if(aseed1 != aseed2){
//Release the created surface
continue; //Do not use this surface/frame since it has artefacts
}

Это, однако, влияет на производительность, поскольку многие фреймы/поверхности отклоняются из-за артефактов. Любые дополнения/исправления к этому будут полезны.