4

私が抱えている問題は、アプリが関数CVPixelBufferRefから取得しようとしたときに、iOS9 sdk でアプリをコンパイルすると、ビデオが読み込まれ、すべてのインスタンスが作成されるときに、時々 null 値を取得することです。AVPlayerItemVideoOutput- copyPixelBufferForItemTime:itemTimeForDisplay:

iOS 8 では私のアプリは正常に動作していましたが、iOS9 では問題が発生しています。iOS 8 SDK でコンパイルされた、ダウンロード可能なアプリ ストアにある私のアプリのバージョンでも、インストール時に同じ問題が発生します。 IOS9。

問題が発生して null が発生したgetCVPixelBufferRef場合、ホームボタンを押してアプリを再度開いたときにアプリがバックグラウンドになりAVPlayerItemVideoOutput、null を与えていたインスタンスがアクティブにCVPixelBufferRefなり、問題が解決されます。

これは、問題を再現するYouTubeビデオです。

https://www.youtube.com/watch?v=997zG08_DMM&feature=youtu.be

すべてのアイテムのインスタンスを作成するためのサンプル コードは次のとおりです。

NSURL *url ;
url = [[NSURL alloc] initFileURLWithPath:[_mainVideo objectForKey:@"file"]];

NSDictionary *pixBuffAttributes = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)};
_videoOutput = [[AVPlayerItemVideoOutput alloc] initWithPixelBufferAttributes:pixBuffAttributes];
_myVideoOutputQueue = dispatch_queue_create("myVideoOutputQueue", DISPATCH_QUEUE_SERIAL);
[_videoOutput setDelegate:self queue:_myVideoOutputQueue];

_player = [[AVPlayer alloc] init];


// Do not take mute button into account
NSError *error = nil;
BOOL success = [[AVAudioSession sharedInstance]
                setCategory:AVAudioSessionCategoryPlayback
                error:&error];
if (!success) {
   // NSLog(@"Could not use AVAudioSessionCategoryPlayback", nil);
}

asset = [AVURLAsset URLAssetWithURL:url options:nil];


if(![[NSFileManager defaultManager] fileExistsAtPath:[[asset URL] path]]) {
   // NSLog(@"file does not exist");
}

NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey, kPlayableKey, nil];

[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:^{

    dispatch_async( dispatch_get_main_queue(),
                   ^{
                       /* Make sure that the value of each key has loaded successfully. */
                       for (NSString *thisKey in requestedKeys)
                       {
                           NSError *error = nil;
                           AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
                           if (keyStatus == AVKeyValueStatusFailed)
                           {
                               [self assetFailedToPrepareForPlayback:error];
                               return;
                           }
                       }

                       NSError* error = nil;
                       AVKeyValueStatus status = [asset statusOfValueForKey:kTracksKey error:&error];
                       if (status == AVKeyValueStatusLoaded)
                       {
                           //_playerItem = [AVPlayerItem playerItemWithAsset:asset];


                           [_playerItem addOutput:_videoOutput];
                           [_player replaceCurrentItemWithPlayerItem:_playerItem];
                           [_videoOutput requestNotificationOfMediaDataChangeWithAdvanceInterval:ONE_FRAME_DURATION];

                           /* When the player item has played to its end time we'll toggle
                            the movie controller Pause button to be the Play button */
                           [[NSNotificationCenter defaultCenter] addObserver:self
                                                                    selector:@selector(playerItemDidReachEnd:)
                                                                        name:AVPlayerItemDidPlayToEndTimeNotification
                                                                      object:_playerItem];

                           seekToZeroBeforePlay = NO;

                           [_playerItem addObserver:self
                                         forKeyPath:kStatusKey
                                            options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                                            context:AVPlayerDemoPlaybackViewControllerStatusObservationContext];

                           [_player addObserver:self
                                     forKeyPath:kCurrentItemKey
                                        options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                                        context:AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];

                           [_player addObserver:self
                                     forKeyPath:kRateKey
                                        options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
                                        context:AVPlayerDemoPlaybackViewControllerRateObservationContext];


                           [self initScrubberTimer];

                           [self syncScrubber];


                       }
                       else
                       {
                         //  NSLog(@"%@ Failed to load the tracks.", self);
                       }
                   });
}];

nullピクセルバッファを提供するサンプルコードを次に示します

CVPixelBufferRef pixelBuffer =
[_videoOutput
 copyPixelBufferForItemTime:[_playerItem currentTime]
itemTimeForDisplay:nil];

NSLog(@"the pixel buffer is %@", pixelBuffer);
NSLog (@"the _videoOutput is %@", _videoOutput.description);
CMTime dataTime = [_playerItem currentTime];
//NSLog(@"the current time is %f", dataTime);
return pixelBuffer;
4

4 に答える 4

1

これは問題の真の解決策ではありませんが (あなたのコメントによると AVFoundation のバグのようです)、Brian Hodge が提案したように 1 秒待つよりも良い回避策を見つけました。配信に失敗した場合は AVPlayer を再作成することですピクセル バッファ。実際の再起動ルーチンの内容によっては、これが大幅に高速化され、ユーザーへの刺激が少ないことが証明される場合があります。また、AVPlayer を起動するたびにではなく、実際に AVPlayer に問題があった場合にのみ (わずかな) 遅延が発生します。

ただし、プレーヤーが再生を終了した後、AVPlayerItemVideoOutput はそれ以上のピクセル バッファーを配信しません。したがって、すでにピクセル バッファを受け取っているかどうかを覚えておくことで、おそらくそのケースを防ぐ必要があります。そうしないと、プレーヤーが意図しないループ再生を実行してしまいます。

クラス インターフェイス:

@property (nonatomic) BOOL videoOutputHadPixelBuffer;

そして、ピクセル バッファをコピーする前に:

if (![self.videoOutput hasNewPixelBufferForItemTime:self.player.currentTime] && !self.videoOutputHadPixelBuffer)
{
    [self restartPlayer]; // call your custom restart routine where you create a new AVPlayer object
}

self.videoOutputHadPixelBuffer = YES; // guard against missing pixel buffers after playback finished
于 2015-11-27T17:30:11.797 に答える
0

*それもあなたのために働いたと言ったので、可能な限り多くの可視性を与えるために、コメントではなく回答として投稿することにしました.

回答: より洗練されたアプローチを探しています。AVPlayerItemVideoOutput alloc の割り当ては、渡すフォーマット設定に関連していることがわかりましたが、かかる時間は絶対的ではありません。割り当てとロード/再生の間の強制的な1秒の待機時間で修正されました。また、AVPlayerItemVideoOutput を 1 つだけ作成して再利用するので、必要な遅延は 1 つだけです。

また:

以下のhasNewPixelBufferForItemTimeの使用は、私が作成したユニティ プラグインの小さなサンプルで、ピクセル バッファの内容をテクスチャにアップロードするだけです。

//////////////////////
 if (g_TexturePointer)
{
    if([plug.playerOutput hasNewPixelBufferForItemTime:[plug.player currentTime]])
    {
        pbuffer = [plug.playerOutput copyPixelBufferForItemTime:plug.player.currentItem.currentTime itemTimeForDisplay:nil];
    } ... .. . (No need to show the rest.)

ハッピーコーディング!

于 2015-11-13T11:41:35.813 に答える
0

arm64今日、同様の問題が発生し、プロジェクトがアーキテクチャ用にビルドされていない場合に、ios 9.0 以降を実行している 64 ビット デバイスでのみ発生することがわかりました。

ビルド設定をarm64 アーキテクチャ用にビルドするように変更すると、この問題が解決しました。

于 2015-10-27T09:21:52.533 に答える