同时下载和播放由多个 URL 拼凑而成的歌曲

Posted

技术标签:

【中文标题】同时下载和播放由多个 URL 拼凑而成的歌曲【英文标题】:Simultaneously downloading and playing a song that is pieced together from multiple URLs 【发布时间】:2013-09-30 20:09:20 【问题描述】:

我有一个 NSArray 字符串,它们是 URL。数组中的每个 URL 都指向与歌曲的一部分相关联的数据。我可以使用以下代码播放整首歌曲,它将歌曲完全下载到单个文件中,然后从该文件中播放:

// Get file path to store song locally
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [NSString stringWithFormat:@"%@/temp.mp3", [paths objectAtIndex:0]];

// Remove current temp file and create a new one
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if ([fileManager fileExistsAtPath:filePath]) [fileManager removeItemAtPath:filePath error:&error];
[fileManager createFileAtPath:filePath contents:nil attributes:nil];
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath];

// Initialize loop variables
NSMutableData *songData = nil;
NSUInteger totalLength = 0;
NSString *localURLString = [NSString stringWithFormat:@"file://%@", [filePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURL *localURL = [NSURL URLWithString:localURLString];

for (NSString *stringURL in urls) 
    NSURL *url = [NSURL URLWithString:stringURL];
    songData = [NSMutableData dataWithContentsOfURL:url];
    totalLength += songData.length;
    [fileHandle writeData:songData];
    [fileHandle synchronizeFile];


[fileHandle closeFile];
self.player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithURL:localURL]];
[self.player play];

现在我正在尝试让整首歌曲无缝播放而无需先下载整个内容(所以基本上是流式传输,但不是从一个 URL 流式传输,我必须在流式传输时将多个歌曲拼凑在一起)。

我有什么选择?

更新 1:

我尝试生成manifest文件,写入文件没有错误,manifestString似乎格式正确,但播放器不播放。

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *manifestPath = [NSString stringWithFormat:@"%@/manifest.m3u8", [paths objectAtIndex:0]];
NSMutableString *manifestString = [[NSMutableString alloc] init];
[manifestString appendString:@"#EXTM3\n"];
for (NSString *stringURL in urls) 
    [manifestString appendFormat:@"%@\n", stringURL];

[manifestString appendString:@"#EXT-X-ENDLIST"];
NSError *err = nil;
[manifestString writeToFile:manifestPath atomically:YES encoding:NSUTF8StringEncoding error:&err];
if (err) 
    NSLog(@"error writing manifest: %@", err);

NSString *manifestURLString = [NSString stringWithFormat:@"file://%@", [filePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURL *manifestURL = [NSURL URLWithString:manifestURLString];
self.player = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithURL:manifestURL]];
[self.player play];

【问题讨论】:

请提供您的 hls 清单。也许你那里有错误 您的清单中缺少以下标签:#EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 此外,它应该是 #EXTM3U 而不是 #EXTM3。我不知道是否需要#EXT-X-TARGETDURATION 标签。此标记的值应该是清单中最长段的长度(向上取整)。 【参考方案1】:

由于您的目标是 ios,您是否考虑过使用 HTTP Live Streaming (HLS)? https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/HTTPStreamingArchitecture/HTTPStreamingArchitecture.html

虽然它通常用于视频 + 音频,但您可以在服务器上创建一个纯音频清单文件,按顺序列出歌曲的每个片段,AVPlayer 会自动为您逐段下载。

例如,我拿了一首歌曲(在一个名为 sample.mp3 的文件中)并将其分段,以便使用 ffmpeg 在 HLS 中使用:

ffmpeg -i sample.mp3 -acodec copy -map 0 -f segment -segment_time 5 -segment_list index.m3u8 segment%05d.mp3

这会获取输入文件 (sample.mp3) 并将其分成 5 秒的片段。它会输出一个清单文件(在本例中为 index.m3u8):

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-ALLOW-CACHE:YES
#EXT-X-TARGETDURATION:6
#EXTINF:5.015511,
segment00000.mp3
#EXTINF:4.989389,
segment00001.mp3
#EXTINF:5.015511,
segment00002.mp3
#EXTINF:4.989389,
segment00003.mp3
#EXTINF:5.015511,
segment00004.mp3
#EXTINF:4.989378,
segment00005.mp3
#EXTINF:4.989389,
segment00006.mp3
#EXTINF:5.015511,
segment00007.mp3
#EXTINF:4.989389,
segment00008.mp3
#EXTINF:5.015511,
segment00009.mp3
#EXTINF:4.989389,
segment00010.mp3
#EXTINF:4.571433,
segment00011.mp3
#EXT-X-ENDLIST

如果您将清单的 URL 传递给 AVPlayer,它可以完成剩下的工作。

【讨论】:

如果我只能访问 URL,这可能吗? IE。我可以在 iOS 端创建清单文件并将其传递给 AVPlayer 吗? 是的,清单来自哪里并不重要。这些段可以通过它们的绝对 URL 来识别(例如 blahblah.com/segment00000.mp3 而不仅仅是 segment00000.mp3)。虽然我认为需要持续时间(#EXTINF)的东西,但我只是试图删除它,它仍然有效。看起来 AVPlayer 很宽容。 好的,我尝试生成清单并以这种方式播放,但播放器似乎没有播放。我用我尝试过的代码更新了问题。 当我下载 m3u8 url 时,我得到了一个 '.movpkg' 路径,如何从同一路径获取 .ts 文件

以上是关于同时下载和播放由多个 URL 拼凑而成的歌曲的主要内容,如果未能解决你的问题,请参考以下文章

2星|《锁脑》:没有实际工作经验和研究成果的写手拼凑而成的理论

如何同时录制使用 AVPlayer 和 wav 文件播放的歌曲?

如何在 python 上同时播放两个不同的歌曲文件?

在 iPhone 上同时播放多首 MP3 歌曲

如何让我的两个音频播放器同时播放两首不同的歌曲?

图例标签组合图(ggplot 和拼凑)