4

これは 2 部構成の質問です。現在のディスプレイ サーフェスを取得し、サーフェスからビデオを作成する次のコードが動作しています (すべてがバックグラウンドで行われます)。

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++;
    }

PS: コードの最後の 4 ~ 5 行が最も関連性があります (フィルター処理が必要な場合)。

1) 作成されたビデオにはアーティファクトがあります。私は以前にビデオに取り組んだことがありますが、以前にもそのような問題に遭遇しました。これには 2 つの理由が考えられると思います
。アダプターに渡された PixelBuffer は、処理 (エンコード + 書き込み) が完了する前に変更または解放されています。これは、非同期呼び出しが原因である可能性があります。しかし、これ自体が問題なのか、それを解決する方法がわかりません。
ii. 渡されたタイムスタンプが不正確です (たとえば、同じタイムスタンプを持つ 2 つのフレーム、または前のフレームよりも低いタイムスタンプを持つフレーム)。タイムスタンプ値をログアウトしましたが、これは問題ではないようです。

2) 上記のコードは、ビデオの再生時またはゲームのプレイ時にサーフェスを取得できません。出力に空白の画面が表示されるだけです。これは、このような場合に発生するハードウェア アクセラレーションによるデコードが原因である可能性があります。

質問の 2 つの部分のいずれかに関する情報は、非常に役立ちます。また、一般的に IOSurfaces で読むための良いリンクがある場合は、ここに投稿してください。

4

1 に答える 1

5

少し実験を行った結果、コンテンツの転送が完了する前 (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
}

ただし、アーティファクトにより多くのフレーム/サーフェスが拒否されるため、これはパフォーマンスに影響します。これに対する追加/修正は役に立ちます。

于 2013-01-04T06:13:28.687 に答える