我正在使用QTKit从URL逐步下载和播放MP3。根据{{3}},这是我应该用来实现的代码:
NSURL *mp3URL = [NSURL URLWithString:@"http://foo.com/bar.mp3"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
这样做,并且完全符合我的要求 - MP3 URL被懒散地下载并立即开始播放。但是,如果URL没有“.mp3”路径扩展名,则会失败:
NSURL *mp3URL = [NSURL URLWithString:@"http://foo.com/bar"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
没有给出错误,没有引发异常;声音的持续时间只是设置为零,没有播放。
我找到解决此问题的唯一方法是通过手动加载数据并使用QTDataReference强制类型:
NSURL *mp3URL = [NSURL URLWithString:@"http://foo.com/bar"];
NSData *mp3Data = [NSData dataWithContentsOfURL:mp3URL];
QTDataReference *dataReference =
[QTDataReference dataReferenceWithReferenceToData:mp3Data
name:@"bar.mp3"
MIMEType:nil];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithDataReference:dataReference error:&error];
[sound play];
然而,这迫使我在开始播放之前同步完全下载所有MP3,这显然是不可取的。有没有办法解决这个问题?
感谢。
实际上,路径扩展似乎与它无关; Content-Type根本没有在HTTP标头中设置。即便如此,后一种代码仍有效,而前者则无效。有人知道解决这个问题的方法,而无法访问服务器吗?
任何?我无法在任何地方找到有关此的信息,而Google令人沮丧地现在将此页面显示为我的大多数查询的最佳结果......
答案 0 :(得分:0)
两个想法。 (第一个有点hacky):
要解决缺少的内容类型,您可以嵌入一个小的Cocoa网络服务器来补充缺少的标题字段,并将NSURL路由到该“代理”。
一些Cocoa http服务器实现:
第二个是切换到较低级别的框架(从QTKit到AudioToolbox)。
你需要更多代码,但是有很多关于如何使用AudioToolbox流式传输mp3的资源
例如。:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
就个人而言,我会选择第二种选择。 AudioToolbox并不像QTKit那么简单,但它为您的问题提供了一个干净的解决方案。它也适用于iOS和Mac OS - 因此您可以找到大量信息。
<强>更新强>
你试过用另一个初始化程序吗?例如
+ (id)movieWithAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr
您可以插入密钥QTMovieURLAttribute
的URL,也可以通过在该字典中提供其他属性来补偿缺少的内容类型。
这个开源项目有一个QTMovie类别,其中包含完成类似事情的方法:
http://vidnik.googlecode.com/svn-history/r63/trunk/Source/Categories/QTMovie+Async.m
答案 1 :(得分:0)
如果您认为weichsel's第一个解决方案是hacky,那么您将爱这个:
罪魁祸首是Content-Type
标题,正如您所确定的那样。如果QTKit.framework
在内部使用了Objective-C,那么使用您选择的类别覆盖-[NSHTTPURLResponse allHeaderFields]
将是一件小事。但是,QTKit.framework
(无论好坏)在内部使用Core Foundation(和Core Services)。这些都是基于C的框架,并且在C中没有优雅的覆盖函数的方法。
那就是说,是一种方法,而不是一种漂亮的方法。 Apple的功能干预甚至是documented,但与其他文档相比,似乎有点落后于时代。
从本质上讲,你想要的东西如下:
typedef struct interpose_s {
void *new_func;
void *orig_func;
} interpose_t;
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
);
static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = {
{ (void *)myCFHTTPMessageCopyHeaderFieldValue, (void *)CFHTTPMessageCopyHeaderFieldValue }
};
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
) {
if (CFStringCompare(headerField, CFSTR("Content-Type"), 0) == kCFCompareEqualTo) {
return CFSTR("audio/x-mpeg");
} else {
return CFHTTPMessageCopyHeaderFieldValue(message, headerField);
}
}
您可能希望在处理Content-Type
字段时添加特定于您的应用程序的逻辑,以免您的应用程序在确定每个HTTP请求为音频文件时以奇怪和奇妙的方式中断。
答案 2 :(得分:0)
尝试将http://
替换为icy://
。
答案 3 :(得分:0)
只需创建一个这样的实例......
QTMovie *aPlayer = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
fileUrl, QTMovieURLAttribute,
[NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
/*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
nil] error:error];