MPMediaItems 原始歌曲数据

发布于 2024-08-09 19:03:21 字数 50 浏览 7 评论 0原文

我想知道如何访问 MPMediaItem 的原始数据。

有什么想法吗?

I was wondering how to access an MPMediaItem's raw data.

Any ideas?

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

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

发布评论

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

评论(3

拧巴小姐 2024-08-16 19:03:21

您可以通过以下方式获取媒体项的数据:

-(void)mediaItemToData
{
    // Implement in your project the media item picker

    MPMediaItem *curItem = musicPlayer.nowPlayingItem;

    NSURL *url = [curItem valueForProperty: MPMediaItemPropertyAssetURL];

    AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];

    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: songAsset
                                       presetName: AVAssetExportPresetPassthrough];

    exporter.outputFileType = @"public.mpeg-4";

    NSString *exportFile = [[self myDocumentsDirectory] stringByAppendingPathComponent:           
                                                               @"exported.mp4"];

    NSURL *exportURL = [[NSURL fileURLWithPath:exportFile] retain];
    exporter.outputURL = exportURL; 

    // do the export
    // (completion handler block omitted) 
    [exporter exportAsynchronouslyWithCompletionHandler:
    ^{
    NSData *data = [NSData dataWithContentsOfFile: [[self myDocumentsDirectory] 
                                     stringByAppendingPathComponent: @"exported.mp4"]];

    // Do with data something

    }];
}

此代码仅适用于 ios 4.0 及更高版本

祝您好运!

you can obtain the media item's data in such way:

-(void)mediaItemToData
{
    // Implement in your project the media item picker

    MPMediaItem *curItem = musicPlayer.nowPlayingItem;

    NSURL *url = [curItem valueForProperty: MPMediaItemPropertyAssetURL];

    AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL: url options:nil];

    AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset: songAsset
                                       presetName: AVAssetExportPresetPassthrough];

    exporter.outputFileType = @"public.mpeg-4";

    NSString *exportFile = [[self myDocumentsDirectory] stringByAppendingPathComponent:           
                                                               @"exported.mp4"];

    NSURL *exportURL = [[NSURL fileURLWithPath:exportFile] retain];
    exporter.outputURL = exportURL; 

    // do the export
    // (completion handler block omitted) 
    [exporter exportAsynchronouslyWithCompletionHandler:
    ^{
    NSData *data = [NSData dataWithContentsOfFile: [[self myDocumentsDirectory] 
                                     stringByAppendingPathComponent: @"exported.mp4"]];

    // Do with data something

    }];
}

This code will work only on ios 4.0 and later

Good luck!

酒与心事 2024-08-16 19:03:21

当然,您可以访问 MPMediaItem 的数据。虽然不是一下子就很清楚,但它确实有效。操作方法如下:

  • 从媒体项的 MPMediaItemPropertyAssetURL 属性获取媒体项的 URL 使用
  • 此 URL 初始化 AVURLAsset 使用此
  • 资源初始化 AVAssetReader
  • 获取 您想要从 AVURLAsset 读取的 AVAssetTrack
  • 使用此轨道创建一个 AVAssetReaderTrackOutput
  • 将此输出添加到之前创建的 AVAssetReader-startReading
  • 使用 AVAssetReaderTrackOutput-copyNextSampleBuffer 获取所有数据,
  • 利润!

这是我的一个项目中的一些示例代码(这不是我的代码宝石,是在我的编码黑暗时代写的):

typedef enum {
  kEDSupportedMediaTypeAAC = 'aac ',
  kEDSupportedMediaTypeMP3 = '.mp3'
} EDSupportedMediaType;

- (EDLibraryAssetReaderStatus)prepareAsset {
  // Get the AVURLAsset
  AVURLAsset *uasset = [m_asset URLAsset];
  
  // Check for DRM protected content
  if (uasset.hasProtectedContent) {
    return kEDLibraryAssetReader_TrackIsDRMProtected;
  }
  
  if ([uasset tracks] == 0) {
    DDLogError(@"no asset tracks found");
    return AVAssetReaderStatusFailed;
  }
  
  // Initialize a reader with a track output
  NSError *err = noErr;
  m_reader = [[AVAssetReader alloc] initWithAsset:uasset error:&err];
  if (!m_reader || err) {
    DDLogError(@"could not create asset reader (%i)\n", [err code]);
    return AVAssetReaderStatusFailed;
  }
  
  // Check tracks for valid format. Currently we only support all MP3 and AAC types, WAV and AIFF is too large to handle
  for (AVAssetTrack *track in uasset.tracks) {
    NSArray *formats = track.formatDescriptions;
    for (int i=0; i<[formats count]; i++) {
      CMFormatDescriptionRef format = (CMFormatDescriptionRef)[formats objectAtIndex:i];
      
      // Check the format types
      CMMediaType mediaType = CMFormatDescriptionGetMediaType(format);
      FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format);
      
      DDLogVerbose(@"mediaType: %s, mediaSubType: %s", COFcc(mediaType), COFcc(mediaSubType));
      if (mediaType == kCMMediaType_Audio) {
        if (mediaSubType == kEDSupportedMediaTypeAAC ||
            mediaSubType == kEDSupportedMediaTypeMP3) {
          m_track = [track retain];
          m_format = CFRetain(format);
          break;
        }
      }
    }
    if (m_track != nil && m_format != NULL) {
      break;
    }
  }
  
  if (m_track == nil || m_format == NULL) {
    return kEDLibraryAssetReader_UnsupportedFormat;
  }
  
  // Create an output for the found track
  m_output = [[AVAssetReaderTrackOutput alloc] initWithTrack:m_track outputSettings:nil];
  [m_reader addOutput:m_output];
  
  // Start reading
  if (![m_reader startReading]) {
    DDLogError(@"could not start reading asset");
    return kEDLibraryAssetReader_CouldNotStartReading;
  }
  
  return 0;
}

- (OSStatus)copyNextSampleBufferRepresentation:(CMSampleBufferRepresentationRef *)repOut {
  pthread_mutex_lock(&m_mtx);
  
  OSStatus err = noErr;
  AVAssetReaderStatus status = m_reader.status;
  
  if (m_invalid) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_Invalidated;
  }
  else if (status != AVAssetReaderStatusReading) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_NoMoreSampleBuffers;
  }
  
  // Read the next sample buffer
  CMSampleBufferRef sbuf = [m_output copyNextSampleBuffer];
  if (sbuf == NULL) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_NoMoreSampleBuffers;
  }
  
  CMSampleBufferRepresentationRef srep = CMSampleBufferRepresentationCreateWithSampleBuffer(sbuf);
  if (srep && repOut != NULL) {
    *repOut = srep;
  }
  else {
    DDLogError(@"CMSampleBufferRef corrupted");
    EDCFShow(sbuf);
    err = kEDLibraryAssetReader_BufferCorrupted;
  }
  CFRelease(sbuf);
  
  pthread_mutex_unlock(&m_mtx);
  
  return err;
}

Of course you can access the data of a MPMediaItem. It's not crystal clear at once but it works. Here's how:

  • Get the media item's URL from it's MPMediaItemPropertyAssetURL property
  • Initialize an AVURLAsset with this URL
  • Initialize an AVAssetReader with this asset
  • Fetch the AVAssetTrack you want to read from the AVURLAsset
  • Create an AVAssetReaderTrackOutput with this track
  • Add this output to the AVAssetReader created before and -startReading
  • Fetch all data with AVAssetReaderTrackOutput's -copyNextSampleBuffer
  • PROFIT!

Here is some sample code from a project of mine (this is not a code jewel of mine, wrote it some time back in my coding dark ages):

typedef enum {
  kEDSupportedMediaTypeAAC = 'aac ',
  kEDSupportedMediaTypeMP3 = '.mp3'
} EDSupportedMediaType;

- (EDLibraryAssetReaderStatus)prepareAsset {
  // Get the AVURLAsset
  AVURLAsset *uasset = [m_asset URLAsset];
  
  // Check for DRM protected content
  if (uasset.hasProtectedContent) {
    return kEDLibraryAssetReader_TrackIsDRMProtected;
  }
  
  if ([uasset tracks] == 0) {
    DDLogError(@"no asset tracks found");
    return AVAssetReaderStatusFailed;
  }
  
  // Initialize a reader with a track output
  NSError *err = noErr;
  m_reader = [[AVAssetReader alloc] initWithAsset:uasset error:&err];
  if (!m_reader || err) {
    DDLogError(@"could not create asset reader (%i)\n", [err code]);
    return AVAssetReaderStatusFailed;
  }
  
  // Check tracks for valid format. Currently we only support all MP3 and AAC types, WAV and AIFF is too large to handle
  for (AVAssetTrack *track in uasset.tracks) {
    NSArray *formats = track.formatDescriptions;
    for (int i=0; i<[formats count]; i++) {
      CMFormatDescriptionRef format = (CMFormatDescriptionRef)[formats objectAtIndex:i];
      
      // Check the format types
      CMMediaType mediaType = CMFormatDescriptionGetMediaType(format);
      FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(format);
      
      DDLogVerbose(@"mediaType: %s, mediaSubType: %s", COFcc(mediaType), COFcc(mediaSubType));
      if (mediaType == kCMMediaType_Audio) {
        if (mediaSubType == kEDSupportedMediaTypeAAC ||
            mediaSubType == kEDSupportedMediaTypeMP3) {
          m_track = [track retain];
          m_format = CFRetain(format);
          break;
        }
      }
    }
    if (m_track != nil && m_format != NULL) {
      break;
    }
  }
  
  if (m_track == nil || m_format == NULL) {
    return kEDLibraryAssetReader_UnsupportedFormat;
  }
  
  // Create an output for the found track
  m_output = [[AVAssetReaderTrackOutput alloc] initWithTrack:m_track outputSettings:nil];
  [m_reader addOutput:m_output];
  
  // Start reading
  if (![m_reader startReading]) {
    DDLogError(@"could not start reading asset");
    return kEDLibraryAssetReader_CouldNotStartReading;
  }
  
  return 0;
}

- (OSStatus)copyNextSampleBufferRepresentation:(CMSampleBufferRepresentationRef *)repOut {
  pthread_mutex_lock(&m_mtx);
  
  OSStatus err = noErr;
  AVAssetReaderStatus status = m_reader.status;
  
  if (m_invalid) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_Invalidated;
  }
  else if (status != AVAssetReaderStatusReading) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_NoMoreSampleBuffers;
  }
  
  // Read the next sample buffer
  CMSampleBufferRef sbuf = [m_output copyNextSampleBuffer];
  if (sbuf == NULL) {
    pthread_mutex_unlock(&m_mtx);
    return kEDLibraryAssetReader_NoMoreSampleBuffers;
  }
  
  CMSampleBufferRepresentationRef srep = CMSampleBufferRepresentationCreateWithSampleBuffer(sbuf);
  if (srep && repOut != NULL) {
    *repOut = srep;
  }
  else {
    DDLogError(@"CMSampleBufferRef corrupted");
    EDCFShow(sbuf);
    err = kEDLibraryAssetReader_BufferCorrupted;
  }
  CFRelease(sbuf);
  
  pthread_mutex_unlock(&m_mtx);
  
  return err;
}
狼性发作 2024-08-16 19:03:21

你不能,而且没有解决方法。 MPMediaItem 不是实际的媒体片段,它只是有关通过 RPC 从另一个进程传递到应用程序的媒体项目的元数据。在您的地址空间中无法访问该项目本身的数据。

我应该注意,即使您有 MPMediaItem,它的数据也可能不会加载到设备内存中。 iPhone 上的闪存速度很慢,内存也很稀缺。虽然 Apple 可能不希望您访问支持 MPMediaItem 的原始数据,但他们很可能不会费心处理它,因为他们不想投入必要的时间来处理 API。如果他们确实提供了对这样的东西的访问,那么它几乎肯定不会作为 NSData,但更有可能作为 NSURL,他们会给你的应用程序允许它打开文件并流式传输数据。

无论如何,如果您想要该功能,您应该提交一份要求的错误报告。

另外,请注意,请勿在发送给 Apple 的错误报告中提及您的年龄。我认为你为手机编写应用程序非常酷,当我在你这个年纪时,我喜欢尝试计算机(当时我正在研究用 Lisp 编写的东西)。问题是你不能在美国合法地同意合同,这就是开发者协议明确禁止你加入的原因。 协议第一段:

您还证明您属于
法定成年年龄
您居住的司法管辖区(位于
许多人至少年满 18 岁
国家)并且您代表您
被法律允许成为
注册 iPhone 开发者。

如果您向 WWDR 代表提及您未达到法定成年年龄,他们可能会意识到您违反了协议,并有义务终止您的开发者帐户。只是一个友好的警告。

You can't, and there are no workaround. An MPMediaItem is not the actual piece of media, it is just the metadata about the media item communicated to the application via RPC from another process. The data for the item itself is not accessible in your address space.

I should note that even if you have the MPMediaItem its data probably is not loaded into the devices memory. The flash on the iPhone is slow and memory is scarce. While Apple may not want you to have access to the raw data backing an MPMediaItem, it is just as likely that they didn't bother dealing with it because they didn't want to invest the time necessary to deal with the APIs. If they did provide access to such a thing it almost certainly would not be as an NSData, but more likely as an NSURL they would give your application that would allow it to open the file and stream through the data.

In any event, if you want the functionality, you should file a bug report asking for.

Also, as a side note, don't mention your age in a bug report you send to Apple. I think it is very cool you are writing apps for the phone, when I was your age I loved experimenting with computers (back then I was working on things written in Lisp). The thing is you cannot legally agree to a contract in the United States, which is why the developer agreement specifically prohibits you from joining. From the first paragraph of the agreement:

You also certify that you are of the
legal age of majority in the
jurisdiction in which you reside (at
least 18 years of age in many
countries) and you represent that you
are legally permitted to become a
Registered iPhone Developer.

If you mention to a WWDR representative that you are not of age of majority they may realize you are in violation of the agreement and be obligated to terminate your developer account. Just a friendly warning.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文