如何使用强制(MP3)类型从URL制作QTMovie播放文件?

时间:2010-12-09 04:55:06

标签: objective-c cocoa mp3 qtkit progressive-download

我正在使用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标头中设置。即便如此,后一种代码仍有效,而前者则无效。有人知道解决这个问题的方法,而无法访问服务器吗?

编辑2

任何?我无法在任何地方找到有关此的信息,而Google令人沮丧地现在将此页面显示为我的大多数查询的最佳结果......

4 个答案:

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