webdevqa.jp.net

iOSの再生中にHLSストリーム(ビデオ)からオーディオを抽出/記録

AVPlayerを使用してHLSストリームを再生しています。また、ユーザーが録音ボタンを押したときにこれらのストリームを録音する必要もあります。私が使用しているアプローチは、オーディオとビデオを別々に記録し、最後にこれらのファイルをマージして最終的なビデオを作成することです。そして、それはリモートのmp4ファイルで成功しています。

しかし、HLS(.m3u8)ファイルの場合、AVAssetWriterを使用してビデオを録画できますが、オーディオの録音に問題があります。

MTAudioProccessingTapを使用して生のオーディオデータを処理し、ファイルに書き込みます。私は this の記事に従いました。リモートのmp4オーディオを録音できますが、HLSストリームでは機能しません。最初は、AVAssetTrack * audioTrack = [asset tracksWithMediaType:AVMediaTypeAudio] [0];を使用してストリームからオーディオトラックを抽出することができませんでした。

しかし、KVOを使用してaudioTracksを抽出し、MTAudioProcessingTapを初期化することができました。

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
AVPlayer *player = (AVPlayer*) object;

if (player.status == AVPlayerStatusReadyToPlay)
{
    NSLog(@"Ready to play");
    self.previousAudioTrackID = 0;


        __weak typeof (self) weakself = self;

        timeObserverForTrack = [player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(1, 100) queue:nil usingBlock:^(CMTime time)
                {

                    @try {

                            for(AVPlayerItemTrack* track in [weakself.avPlayer.currentItem tracks]) {
                                if([track.assetTrack.mediaType isEqualToString:AVMediaTypeAudio])
                                    weakself.currentAudioPlayerItemTrack = track;

                            }

                            AVAssetTrack* audioAssetTrack = weakself.currentAudioPlayerItemTrack.assetTrack;


                            weakself.currentAudioTrackID = audioAssetTrack.trackID;

                            if(weakself.previousAudioTrackID != weakself.currentAudioTrackID) {

                                NSLog(@":::::::::::::::::::::::::: Audio track changed : %d",weakself.currentAudioTrackID);
                                weakself.previousAudioTrackID = weakself.currentAudioTrackID;
                                weakself.audioTrack = audioAssetTrack;
                                /// Use this audio track to initialize MTAudioProcessingTap
                            }
                        }
                        @catch (NSException *exception) {
                            NSLog(@"Exception Trap ::::: Audio tracks not found!");
                        }

                }];


    }
}  

トラックが変更されたかどうかを確認するために、trackIDも追跡しています。

これが、MTAudioProcessingTapを初期化する方法です。

-(void)beginRecordingAudioFromTrack:(AVAssetTrack *)audioTrack{
// Configure an MTAudioProcessingTap to handle things.
MTAudioProcessingTapRef tap;
MTAudioProcessingTapCallbacks callbacks;
callbacks.version = kMTAudioProcessingTapCallbacksVersion_0;
callbacks.clientInfo = (__bridge void *)(self);
callbacks.init = init;
callbacks.prepare = prepare;
callbacks.process = process;
callbacks.unprepare = unprepare;
callbacks.finalize = finalize;

OSStatus err = MTAudioProcessingTapCreate(
    kCFAllocatorDefault, 
    &callbacks, 
    kMTAudioProcessingTapCreationFlag_PostEffects, 
    &tap
);

if(err) {
    NSLog(@"Unable to create the Audio Processing Tap %d", (int)err);
    NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain
                                         code:err
                                     userInfo:nil];
    NSLog(@"Error: %@", [error description]);;
    return;
}

// Create an AudioMix and assign it to our currently playing "item", which
// is just the stream itself.


AVMutableAudioMix *audioMix = [AVMutableAudioMix audioMix];
AVMutableAudioMixInputParameters *inputParams = [AVMutableAudioMixInputParameters
    audioMixInputParametersWithTrack:audioTrack];

inputParams.audioTapProcessor = tap;
audioMix.inputParameters = @[inputParams];
_audioPlayer.currentItem.audioMix = audioMix;
}

しかし、このオーディオトラックでは、MTAudioProcessingTapコールバック「Prepare」と「Process」が呼び出されることはありません。

KVO経由で取得したaudioTrackに問題はありますか?

今、私は誰かがこれを手伝ってくれると本当にありがたいです。それとも、書き込みアプローチを使用してHLSストリームを記録しているのでしょうか?

10
Sajad Khan

私はこれに対する解決策を見つけ、それを自分のアプリで使用しました。もっと早く投稿したかったのですが、時間が取れませんでした。

したがって、HLSで遊ぶには、それらが正確に何であるかについてある程度の知識が必要です。これについては、Apple Website。 HLS Apple をご覧ください。

これが私が従うステップです。 1.最初にm3u8を取得して解析します。この便利なキット M3U8Kit を使用して解析できます。このキットを使用すると、M3U8MediaPlaylistまたはM3U8MasterPlaylist(マスタープレイリストの場合)を取得できます。マスタープレイリストを取得した場合は、それを解析してM3U8MediaPlaylistを取得することもできます。

(void) parseM3u8
{
NSString *plainString = [self.url m3u8PlanString];
BOOL isMasterPlaylist = [plainString isMasterPlaylist];


NSError *error;
NSURL *baseURL;

if(isMasterPlaylist)
{

    M3U8MasterPlaylist *masterList = [[M3U8MasterPlaylist alloc] initWithContentOfURL:self.url error:&error];
    self.masterPlaylist = masterList;

    M3U8ExtXStreamInfList *xStreamInfList = masterList.xStreamList;
    M3U8ExtXStreamInf *StreamInfo = [xStreamInfList extXStreamInfAtIndex:0];

    NSString *URI = StreamInfo.URI;
    NSRange range = [URI rangeOfString:@"dailymotion.com"];
    NSString *baseURLString = [URI substringToIndex:(range.location+range.length)];
    baseURL = [NSURL URLWithString:baseURLString];

    plainString = [[NSURL URLWithString:URI] m3u8PlanString];
}



M3U8MediaPlaylist *mediaPlaylist = [[M3U8MediaPlaylist alloc] initWithContent:plainString baseURL:baseURL];
self.mediaPlaylist = mediaPlaylist;

M3U8SegmentInfoList *segmentInfoList = mediaPlaylist.segmentList;

NSMutableArray *segmentUrls = [[NSMutableArray alloc] init];

for (int i = 0; i < segmentInfoList.count; i++)
{
    M3U8SegmentInfo *segmentInfo = [segmentInfoList segmentInfoAtIndex:i];

    NSString *segmentURI = segmentInfo.URI;
    NSURL *mediaURL = [baseURL URLByAppendingPathComponent:segmentURI];

    [segmentUrls addObject:mediaURL];
    if(!self.segmentDuration)
        self.segmentDuration = segmentInfo.duration;
}

self.segmentFilesURLs = segmentUrls;



}

M3u8が.tsファイルを解析して、.tsファイルへのリンクを取得することがわかります。

  1. 次に、すべての.tsファイルをローカルフォルダーにダウンロードします。
  2. これらの.tsファイルを1つのmp4ファイルにマージして、エクスポートします。あなたはこの素晴らしいCライブラリを使用してそれを行うことができます TS2MP4

次に、.tsファイルを削除するか、必要に応じて保持します。

8
Sajad Khan

これは、M3U8リンクを解析して、セグメントファイル(.ts)をダウンロードすることをお勧めします。これらのファイルを取得できる場合は、それらをマージしてmp4ファイルを生成できます。

2
Aamir