在播放 iOS 时从 HLS 流(视频)中提取/录制音频

Posted

技术标签:

【中文标题】在播放 iOS 时从 HLS 流(视频)中提取/录制音频【英文标题】:Extract/Record Audio from HLS stream (video) while playing iOS 【发布时间】:2015-10-20 09:54:32 【问题描述】:

我正在使用 AVPlayer 播放 HLS 流。而且我还需要在用户按下记录按钮时记录这些流。 我使用的方法是分别录制音频和视频,然后最后合并这些文件以制作最终视频。并且远程 mp4 文件是成功的。

但现在对于 HLS (.m3u8) 文件,我可以使用 AVAssetWriter 录制视频,但在录制音频时遇到问题。

我正在使用 MTAudioProccessingTap 处理原始音频数据并将其写入文件。我关注了this 文章。我能够录制远程 mp4 音频,但它不适用于 HLS 流。 最初,我无法使用 AVAssetTrack *audioTrack = [asset trackingWithMediaType:AVMediaTypeAudio][0] 从流中提取音轨;

但我能够使用 KVO 提取音频轨道来初始化 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 以检查 track 是否已更改。

这就是我初始化 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 回调“准备”和“处理”永远不会被调用。

我通过 KVO 获得的 audioTrack 有问题吗?

如果有人能帮我解决这个问题,我将不胜感激。或者可以告诉我是否使用写入方法来记录 HLS 流?

【问题讨论】:

你使用m3u8文件创建mp4文件成功了吗? @Milan 我已经发布了答案。请检查。 你能分享一个录制直播的代码吗 【参考方案1】:

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

因此,要使用 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 文件的链接。

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

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

【讨论】:

如果 m3u8 是 ts 段的静态列表,这将很有帮助。但是,如果您想录制实时流,这将不起作用,因为当您定期访问服务器时,您必须管理重复项。即使这意味着聘请顾问,我也非常需要这个。 是的 Carlos F,你是对的,你必须管理它,因为当玩家首先访问服务器然后你稍后尝试解析时,这也会造成混乱。但我确实也使用这种方法录制了直播。 @SajadKhan 您能否详细说明答案。上面的答案不清楚。或者如果你在上面做一个演示项目会更感激。【参考方案2】:

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

【讨论】:

以上是关于在播放 iOS 时从 HLS 流(视频)中提取/录制音频的主要内容,如果未能解决你的问题,请参考以下文章

EasyNVR视频广场按需播放HLS直播流总是断流的原因

EasyNVR视频广场按需播放HLS直播流总是断流原因排查

HLS 流 HTML5 视频 - 缓冲 X 时间后刷新

Hls 流 url 不会在 AVPlayer 中播放

Qt推流程序自动生成网页远程查看实时视频流(视频文件/视频流/摄像头/桌面转成流媒体rtmp+hls+webrtc)

使用HLS协议连接nginx实现近实时流方式播放视频