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

发布于 2024-10-06 10:30:28 字数 1468 浏览 11 评论 0 原文

我正在使用 QTKit 从 URL 逐步下载和播放 MP3。根据此文档,这是我应该用来完成此任务的代码:

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,这显然是不可取的。有什么办法解决这个问题吗?

谢谢。

编辑

实际上,路径扩展似乎与它无关;根本没有在 HTTP 标头中设置 Content-Type。即便如此,后一个代码可以工作,而前一个则不能。有人知道在无法访问服务器的情况下解决此问题的方法吗?

编辑2

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

I'm using QTKit to progressively download and play an MP3 from a URL. According to this documentation, this is the code I should use to accomplish that:

NSURL *mp3URL = [NSURL URLWithString:@"http://foo.com/bar.mp3"];

NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];

This works, and does exactly what I want — the MP3 URL is lazily downloaded and starts playing immediately. However, if the URL does not have the ".mp3" path extension, it fails:

NSURL *mp3URL = [NSURL URLWithString:@"http://foo.com/bar"];

NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];

No error is given, no exception is raised; the duration of the sound is just set to zero, and nothing plays.

The only way I have found to work around this is to force a type by loading the data manually and using a 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];

However, this forces me to completely download ALL of the MP3 synchronously before I can start playing it, which is obviously undesirable. Is there any way around this?

Thanks.

Edit

Actually, it seems that the path extension has nothing to do with it; the Content-Type is simply not being set in the HTTP header. Even so, the latter code works and the former does not. Anyone know of a way to fix this, without having access to the server?

Edit 2

Anyone? I can't find information about this anywhere, and Google frustratingly now shows this page as the top result for most of my queries...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝颜夕 2024-10-13 10:30:28

两个想法。 (第一个有点hacky):
要解决缺少的内容类型,您可以嵌入一个小型 Cocoa Web 服务器来补充缺少的标头字段,并通过该“代理”路由您的 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

Two ideas. (The first one being a bit hacky):
To work around the missing content type, you could embed a small Cocoa webserver that supplements the missing header field and route your NSURL over that "proxy".

Some Cocoa http server implementations:

The second one would be, to switch to a lower level framework (From QTKit to AudioToolbox).
You'd need more code, but there are some very good resources out there on how to stream mp3 using AudioToolbox.
e.g.:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html

Personally I'd go with the second option. AudioToolbox isn't as straightforward as QTKit but it offers a clean solution to your problem. It's also available on both - iOS and Mac OS - so you will find plenty of information.

Update:
Did you try to use another initializer? e.g.

+ (id)movieWithAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr

You can insert your URL for the key QTMovieURLAttribute and maybe you can compensate the missing content type by providing other attributes in that dictionary.
This open source project has a QTMovie category that contains methods to accomplish similar things:
http://vidnik.googlecode.com/svn-history/r63/trunk/Source/Categories/QTMovie+Async.m

但可醉心 2024-10-13 10:30:28

如果您认为 weichsel 的 第一个解决方案很糟糕,您会喜欢这个:

罪魁祸首是 Content-Type 标头,正如您所确定的。如果 QTKit.framework 在内部使用 Objective-C,那么用您选择的类别覆盖 -[NSHTTPURLResponse allHeaderFields] 就很简单了。然而,QTKit.framework(无论好坏)在内部使用 Core Foundation(和 Core Services)。这些都是基于 C 的框架,并且没有优雅的方法来重写 C 中的函数。

也就是说,有一种方法,只是不是一个漂亮的方法。函数插入甚至是 由 Apple 记录,但与他们的文档的其余部分相比,似乎有点落后于时代。

本质上,您需要遵循以下几行:

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 请求时以奇怪而奇妙的方式中断被确定为音频文件。

If you thought weichsel's first solution was hacky, you're going to love this one:

The culprit is the Content-Type header, as you have determined. Had QTKit.framework used Objective-C internally, this would be a trivial matter of overriding -[NSHTTPURLResponse allHeaderFields] with a category of your choosing. However, QTKit.framework (for better or worse) uses Core Foundation (and Core Services) internally. These are both C-based frameworks and there is no elegant way of overriding functions in C.

That said, there is a method, just not a pretty one. Function interposition is even documented by Apple, but seems to be a bit behind the times, compared to the remainder of their documentation.

In essence, you want something along the following lines:

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);
    }
}

You might want to add logic specific to your application in terms of handling the Content-Type field lest your application break in weird and wonderful ways when every HTTP request is determined to be an audio file.

疯狂的代价 2024-10-13 10:30:28

尝试将 http:// 替换为 icy://

Try replacing http:// with icy://.

云归处 2024-10-13 10:30:28

只需创建一个像这样的实例...

QTMovie *aPlayer  = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                 fileUrl, QTMovieURLAttribute,
                                                 [NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
                                                 /*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
                                                 nil] error:error];

Just create an instance like this...

QTMovie *aPlayer  = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                 fileUrl, QTMovieURLAttribute,
                                                 [NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
                                                 /*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
                                                 nil] error:error];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文