同时下载和播放由多个URL拼凑而成的歌曲 [英] Simultaneously downloading and playing a song that is pieced together from multiple URLs

查看:67
本文介绍了同时下载和播放由多个URL拼凑而成的歌曲的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个NSURL字符串,它们是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进行流式传输,我必须在流式传输时将多个歌曲拼凑在一起)./p>

我有什么选择?

更新1:

我尝试生成清单文件,写入文件没有错误,并且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];

解决方案

自从您定位到iOS之后,您是否考虑过使用HTTP Live Streaming(HLS)? https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/HTTPStreamingArchitecture/HTTPStreamingArchitecture.html

尽管它通常用于视频+音频,但是您可以在服务器上创建一个仅音频的清单文件,该文件依次列出歌曲的每个片段,AVPlayer将负责为您自动逐段下载./p>

例如,我取了一首歌(在一个名为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,则其余的工作就可以完成.

I have an NSArray of strings that are URLs. Each URL in the array points to data that is associated with a section of the song. I can play the full song with the following code, which fully downloads the song to a single file then plays it from that file:

// 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];

Now I'm trying to get the entire song to play seamlessly without downloading the whole thing first (so basically streaming it, but instead of streaming from one URL I have to piece the song together from multiple while I stream).

What options do I have?

UPDATE 1:

I've tried to generate the manifest file, there is no error writing the file, and the manifestString seems to be in the correct format, but the player does not play.

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];

解决方案

Since you're targeting iOS, have you considered using HTTP Live Streaming (HLS)? https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/HTTPStreamingArchitecture/HTTPStreamingArchitecture.html

Although it's typically used for video + audio, you can create an audio-only manifest file on your server that lists each of the segments of the song sequentially and AVPlayer will take care of downloading it piece by piece automatically for you.

For example, I took a song (in a file called sample.mp3) and segmented it for use in HLS with ffmpeg:

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

This takes the input file (sample.mp3) and splits it into 5 second segments. It spits out a manifest file (index.m3u8 in this case):

#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

If you pass a URL to the manifest to AVPlayer, it can do the rest.

这篇关于同时下载和播放由多个URL拼凑而成的歌曲的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆