Extract/Record 播放时来自 HLS 流(视频)的音频 iOS

Extract/Record Audio from HLS stream (video) while playing iOS

我正在使用 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 流?

这不是一个好方法,您可以做的是解析 M3U8 link。然后尝试下载段文件 (.ts)。如果你能得到这些文件,你可以将它们合并生成mp4文件。

我找到了解决方案并在我的应用程序中使用它。想早点 post 但没时间。

因此,要使用 HLS,您应该了解它们到底是什么。为此,请在 Apple 网站上查看。 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 文件的链接。

  1. 现在将所有 .ts 文件下载到本地文件夹中。
  2. 将这些.ts 文件合并为一个mp4 文件并导出。 你可以使用这个很棒的 C 库来做到这一点 TS2MP4

然后您可以删除 .ts 文件或在需要时保留它们。