web-dev-qa-db-ja.com

AVAssetReaderを使用してリモートアセットから読み取り(ストリーミング)

私の主な目標は、サーバーからビデオをストリーミングし、ストリーミング中にフレームごとにカットすることです(OpenGLで使用できるようにするため)。そのために、私はインターネット上のいたるところにあるこのコードを使用しました(AppleのGLVideoFrameサンプルコードからのものだったことを思い出します)。

NSArray * tracks = [asset tracks];
NSLog(@"%d", tracks.count);

for(AVAssetTrack* track in tracks) {

    NSLog(@"type: %@", [track mediaType]);

    initialFPS = track.nominalFrameRate;
    width = (GLuint)track.naturalSize.width;
    height = (GLuint)track.naturalSize.height;


    NSError * error = nil;

    // _movieReader is a member variable
    @try {
        self._movieReader = [[[AVAssetReader alloc] initWithAsset:asset error:&error] autorelease];
    }
    @catch (NSException *exception) {
        NSLog(@"%@ -- %@", [exception name], [exception reason]);
        NSLog(@"skipping track");

        continue;
    }


    if (error)
    {
        NSLog(@"CODE:%d\nDOMAIN:%@\nDESCRIPTION:%@\nFAILURE_REASON:%@", [error code], [error domain], error.localizedDescription, [error localizedFailureReason]);                                          
        continue;
    }

    NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey;
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [_movieReader addOutput:[AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track
                                                                       outputSettings:videoSettings]];
    [_movieReader startReading];
    [self performSelectorOnMainThread:@selector(frameStarter) withObject:nil waitUntilDone:NO];
}

しかし、私は常に[[AVAssetReader alloc] initWithAsset:error:]でこの例外を受け取ります。

NSInvalidArgumentException -- *** -[AVAssetReader initWithAsset:error:] Cannot initialize an instance of AVAssetReader with an asset at non-local URL 'http://devimages.Apple.com/iphone/samples/bipbop/bipbopall.m3u8'

だから私の2つの質問は次のとおりです。

  1. 例外は、AVAssetReaderにローカルURLが必要であることを本当に教えてくれますか?ストリーミングに使用できますか(他のAVFoundationクラスと同じように)?
  2. AVFoundationアプローチが機能しない場合、ビデオのストリーミングとフレームの分割を同時に行うための他の提案は何ですか?

あなたの助けをどうもありがとう。

30
Paula Mamdouh

AVFoundationは、使用されているファイルやプロトコルの種類を区別するほど、ローカルファイルと非ローカルファイルを区別していないようです。 mp4/movを使用することとm3u8を介してHTTPLiveストリーミングプロトコルを使用することには非常に明確な違いがありますが、ローカルまたはリモートのmp4を使用する場合の違いは少しあいまいです。

上記を拡張するには:

a)「リモート」アセットがM3U8である場合(つまり、HTTP「ライブ」ストリーミングを使用している場合)、チャンスはまったくありません。 M3U8がローカルファイルシステムにあるかリモートサーバーにあるかに関係なく、さまざまな理由により、AVAssetReaderおよびすべてのAVAsset関連機能が機能しません。 However, AVPlayer, AVPlayerItem etc would work just fine.

b)MP4/MOVの場合は、もう少し調査が必要です。 Local MP4/MOV's work flawlessly.リモートMP4/MOVの場合、AVAssetReaderを正常に初期化できるAVURLAssetを作成(またはAVPlayerItem、AVPlayer、またはAVAssetTracksから取得)することができます(「時々」も、まもなく)。しかしながら、 copyNextSampleBuffer always returns nil in case of remote MP4's。 copyNextSampleBufferを呼び出すまでにいくつかのことが機能するため、次の場合は100%確信が持てません。

i)他のすべての手順が成功した後、リモートmp4でcopyNextSampleBufferが機能しないのは、意図された/期待される機能です。

ii)「他のステップ」がリモートMP4でまったく機能しているように見えるのは、Appleの実装の偶然であり、copyNextSampleBufferを押すと、この非互換性が前面に出てきます............。これらの「その他のステップ」とは何か、後で詳しく説明します。

iii)リモートMP4のcopyNextSampleBufferを呼び出そうとすると、何か問題が発生します。

したがって、@ Paulaは、リモートMOV/MP4を使用してもう少し調査を試みることができます。

参考までに、ビデオからフレームをキャプチャするために試したアプローチは次のとおりです。

a)

ビデオURLから直接AVURLAssetを作成します。

[assettracksWithMediaType:AVMediaTypeVideo]を使用してビデオトラックを取得します

ビデオトラックをソースとして使用して、AVAssetReaderTrackOutputを準備します。

AVURLAssetを使用してAVAssetReaderを作成します。

AVAssetReaderTrackOutputをAVAssetReaderに追加し、Readingを開始します。

CopyNextSampleBufferを使用して画像を取得します。

b)

ビデオURLからAVPlayerItemを作成し、次にそれからAVPlayerを作成します(またはURLから直接AVPlayerを作成します)。

AVPlayerの「asset」プロパティを取得し、「loadValuesAsynchronouslyForKeys:」を使用してその「トラック」をロードします。

タイプAVMediaTypeVideoのトラックを分離し(または、トラックがロードされたらアセットでtracksWithMediaType:を呼び出す)、ビデオトラックを使用してAVAssetReaderTrackOutputを作成します。

AVPlayerの「asset」、「startReading」を使用してAVAssetReaderを作成し、copyNextSampleBufferを使用して画像を取得します。

c)

ビデオURLから直接AVPlayerItem + AVPlayerまたはAVPlayerを作成します。

AVPlayerItemの「tracks」プロパティをKVOし、トラックがロードされたら、タイプAVMediaTypeVideoのAVAssetTracksを分離します。

AVPlayerItem/AVPlayer/AVAssetTrackの「asset」プロパティからAVAssetを取得します。

残りの手順は、アプローチ(b)と同様です。

d)

ビデオURLから直接AVPlayerItem + AVPlayerまたはAVPlayerを作成します。

AVPlayerItemの「tracks」プロパティをKVOし、トラックがロードされたら、タイプAVMediaTypeVideoのトラックを分離します。

AVMutableCompositionを作成し、タイプAVMediaTypeVideoの関連するAVMutableCompositionTrackを初期化します。

以前に取得したビデオトラックから適切なCMTimeRangeをこのAVMutableCompositionTrackに挿入します。

(b)および(c)と同様に、AVAssetReaderおよびAVAssetReaderTrackOutputを作成しますが、AVAssetReaderを初期化するためのベースAVAssetとしてAVMutableCompositionを使用し、AVAssetReaderTrackOutputのベースAVAssetTrackとしてAVMutableCompositionTrackを使用する点が異なります。

'startReading'を実行し、copyNextSampleBufferを使用してAVAssetReaderからフレームを取得します。

追伸:AVPlayerItemまたはAVPlayerから直接取得したAVAssetが動作していないという事実を回避するために、ここでアプローチ(d)を試しました。そこで、すでに手元にあるAVAssetTracksから新しいAVAssetを作成したいと思いました。確かにハッキーで、おそらく無意味です(元のAVAssetでなければ、トラック情報は最終的にどこから取得されますか!)が、とにかく必死に試す価値がありました。

さまざまな種類のファイルの結果の概要は次のとおりです。

1)ローカルMOV/MP4-4つのアプローチすべてが問題なく機能します。

2)リモートMOV/MP4-アセットとトラックはアプローチ(b)から(d)で正しく取得され、AVAssetReaderも初期化されますが、copyNextSampleBufferは常にnilを返します。 (a)の場合、AVAssetReader自体の作成は「不明なエラー」NSOSStatusErrorDomain-12407で失敗します。

3)ローカルM3U8(アプリ内/ローカルHTTPサーバーを介してアクセス)-アプローチ(a)、(b)、および(c)は、M3U8を介してストリーミングされるファイルの任意の形状または形式でAVURLAsset/AVAssetを取得しようとすると、惨めに失敗します。愚か者の用事。

(a)の場合、アセットはまったく作成されず、AVURLAssetでのinitWithURL:呼び出しは「不明なエラー」AVFoundationErrorDomain-11800で失敗します。

(b)および(c)の場合、AVPlayer/AVPlayerItemまたはAVAssetTracksからAVURLAssetを取得すると、SOMEオブジェクトが返されますが、そのオブジェクトの「tracks」プロパティにアクセスすると、常に空の配列が返されます。

(d)の場合、ビデオトラックを正常に取得して分離できますが、AVMutableCompositionTrackを作成しようとすると、CMTimeRangeをソーストラックからAVMutableCompositionTrackに挿入しようとすると失敗し、「不明なエラー」が発生します。 NSOSStatusErrorDomain-12780。

4)リモートM3U8は、ローカルM3U8とまったく同じように動作します。

私はこれらの違いが存在する理由について完全に教育されていないか、Appleによって軽減できなかったでしょう。しかし、そこに行きます。

36
Dev Kanchen

AVMutableCompositionTrackでリモートファイルを取得できます

AVURLAsset* soundTrackAsset = [[AVURLAsset alloc]initWithURL:[NSURL URLWithString:@"http://www.yoururl.com/yourfile.mp3"] options:nil];

AVMutableCompositionTrack *compositionAudioSoundTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[compositionAudioSoundTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset.duration) 
                               ofTrack:[[soundTrackAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] 
                                atTime:kCMTimeZero error:nil];

ただし、このアプローチは、MP4のように圧縮率が高いファイルではうまく機能しません。

0
Julio Bailon