从NSData解码MP3文件 [英] Decode MP3 File from NSData

查看:160
本文介绍了从NSData解码MP3文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的应用程序,我需要解码存储在NSData对象中的MP3文件。

For my application, I need to decode an MP3 file which is stored in an NSData object.

出于安全考虑,不希望将NSData对象写入磁盘并使用系统URL引用重新打开它,即使它只在本地存储了一会儿。

For security reasons, it is undesirable to write the NSData object to disk and re-open it using a System URL reference, even if its only locally stored for a few moments.

我想利用扩展音频文件服务(或音频文件服务)这样做,但我无法获得NSData的表示,NSData只存在于内存中,可由这些音频文件服务读取。

I would like to take advantage Extended Audio File Services (or Audio File Services) to do this, but I'm having trouble getting a representation of the NSData, which exists only in memory, that can be read by these Audio File Services.

编辑:我想解码MP3数据,这样我就可以访问线性PCM音频样本进行操作。从NSData对象回放不是问题。

我的代码如下:

 decryptedData; //an NSData object which has already been initialized
 const void *dataBytes   = decryptedData.bytes; //pointer to the bytes in my NSData object 

//this creates a CFURLRef from the pointer to the byte data
//I have printed out the resulting CFURL and have confirmed that it is indeed reading the bytes correctly
CFURLRef audioFileURLFromBytes = CFURLCreateWithBytes (kCFAllocatorDefault,
                                                               dataBytes,
                                                               decryptedData.length,
                                                               kCFStringEncodingASCII,
                                                               NULL);

//attempt to open the the URL using Extended Audio File Services
        ExtAudioFileRef outExtAudioFile;
        OSStatus err = 0;
        err = ExtAudioFileOpenURL(audioFileURLFromBytes, &outExtAudioFile);
        if (err != noErr) {
            NSLog(@"ExtAudioFileOpenURL failed with OSStatus Code %i \n", err);
        }

//Attempt to open the URL using Audio File Services
        AudioFileID audioFile;
        OSStatus res = 0;
        res = AudioFileOpenURL(audioFileURLFromBytes, kAudioFileReadPermission,  kAudioFileMP3Type, &audioFile);
        if (res != noErr) {
            NSLog(@"AudioFileOpenURL failed with OSStatus Code %i \n", res);
        }

两次尝试打开URL都会导致OSStatus Code 43,找不到文件。

Both attempts at opening the URL result in an OSStatus Code 43, which is "file not found".

我已经确认我的指针指向NSData内存中的正确地址,并且可以正确读取字节。

I have verified that my pointer is pointing to the correct address in memory for the NSData and that the bytes can be read correctly.

扩展音频文件服务是否有一些限制禁止引用存储在内存中的字节?

Is there some limitation to the Extended Audio File Services that prohibit references to bytes stored in memory?

感谢您的帮助可以提供。

Thanks for any help you can provide.

编辑:我想出了如何使用Sbooth的建议来做到这一点。代码如下:
此函数采用包含音频文件的mp3表示的NSData对象。它将其解码为线性PCM,因此您可以获取样本,然后将其重新编码为AAC。我不认为CoreAudio中的MP3编码可用于所有平台(移动/桌面)。此代码在我的Mac上进行了测试并完成了工作。

I figured out how to do it using Sbooth's suggestion. Code below: This function takes an NSData object containing an mp3 representation of an audio file. It decodes it as linear PCM so you can get the samples and then re-encodes it as AAC. I don't think MP3 encoding is available in CoreAudio across all platforms (mobile/desktop). This code was tested on my Mac and gets the job done.

-(void) audioFileReaderWithData: (NSData *) audioData {

        AudioFileID         refAudioFileID;
        ExtAudioFileRef     inputFileID;
        ExtAudioFileRef     outputFileID;

        OSStatus result = AudioFileOpenWithCallbacks(audioData, readProc, 0, getSizeProc, 0, kAudioFileMP3Type, &refAudioFileID);
        if(result != noErr){
            NSLog(@"problem in theAudioFileReaderWithData function: result code %i \n", result);
        }

        result = ExtAudioFileWrapAudioFileID(refAudioFileID, false, &inputFileID);
        if (result != noErr){
            NSLog(@"problem in theAudioFileReaderWithData function Wraping the audio FileID: result code %i \n", result);
        }

        // Client Audio Format Description
    AudioStreamBasicDescription clientFormat;
    memset(&clientFormat, 0, sizeof(clientFormat));
    clientFormat.mFormatID          = kAudioFormatLinearPCM;
    clientFormat.mFramesPerPacket   = 1;
    clientFormat.mChannelsPerFrame  = 2;
    clientFormat.mBitsPerChannel    = 32;
    clientFormat.mBytesPerPacket    = clientFormat.mBytesPerFrame = 4 *   clientFormat.mChannelsPerFrame;
    clientFormat.mFormatFlags       = kAudioFormatFlagsNativeFloatPacked;
    clientFormat.mSampleRate        = 44100;

     //Output Audio Format Description
     AudioStreamBasicDescription outputFormat;
     memset(&outputFormat, 0, sizeof(outputFormat));
     outputFormat.mChannelsPerFrame  = 2;
     outputFormat.mSampleRate        = 44100;
     outputFormat.mFormatID          = kAudioFormatMPEG4AAC;
     outputFormat.mFormatFlags       = kMPEG4Object_AAC_Main;
     outputFormat.mBitsPerChannel    = 0;
     outputFormat.mBytesPerFrame     = 0;
     outputFormat.mBytesPerPacket    = 0;
     outputFormat.mFramesPerPacket   = 1024;

     // create the outputFile that we're writing to here....
     UInt32 outputFormatSize = sizeof(outputFormat);
     result = 0;
     result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat);
     if(result != noErr)
            NSLog(@"could not set the output format with status code %i \n",result);

     NSMutableString *outputFilePath = [NSMutableString stringWithCapacity: 100];
     [outputFilePath setString:@"/Users/You/Desktop/testAudio.m4a"];
     NSURL *sourceURL = [NSURL fileURLWithPath:outputFilePath];

     result      =  0;
     result      =  ExtAudioFileCreateWithURL((CFURLRef)sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID);
     if(result != noErr){
           NSLog(@"ExtAudioFileCreateWithURL failed for outputFileID with status %i \n", result);
      }

     int size = sizeof(clientFormat);
     result = 0;
     result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);

     if(result != noErr)
        NSLog(@"error on ExtAudioFileSetProperty for input File with result code %i \n", result);

     size = sizeof(clientFormat);
     result = 0;
     result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
     if(result != noErr)
         NSLog(@"error on ExtAudioFileSetProperty for output File with result code %i \n", result);

     int totalFrames = 0;
     UInt32 outputFilePacketPosition = 0; //in bytes  
     UInt32 encodedBytes = 0;

     while (1) {
        UInt32 bufferByteSize       = 22050 * 4 * 2;
        char srcBuffer[bufferByteSize];
        UInt32 numFrames            = (bufferByteSize/clientFormat.mBytesPerFrame);

        AudioBufferList fillBufList;
        fillBufList.mNumberBuffers  = 1;
        fillBufList.mBuffers[0].mNumberChannels     = clientFormat.mChannelsPerFrame;
        fillBufList.mBuffers[0].mDataByteSize       = bufferByteSize;
        fillBufList.mBuffers[0].mData               = srcBuffer;
        result = 0;
        result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList);

        if (result != noErr) {
            NSLog(@"Error on ExtAudioFileRead with result code %i \n", result);
                totalFrames = 0;
            break;
        }
        if (!numFrames)
            break;

        totalFrames = totalFrames + numFrames;

        result = 0;
        result = ExtAudioFileWrite(outputFileID,
                                   numFrames,
                                   &fillBufList);

        if(result!= noErr){
            NSLog(@"ExtAudioFileWrite failed with code %i \n", result);
        }

        encodedBytes += numFrames  * clientFormat.mBytesPerFrame;
    }

    //Clean up

    ExtAudioFileDispose(inputFileID);
    ExtAudioFileDispose(outputFileID);
    AudioFileClose(refAudioFileID);

}

你还需要这些功能......

And you'll need these functions as well...

static OSStatus readProc(void* clientData,
                         SInt64 position,
                         UInt32 requestCount,
                         void* buffer,
                         UInt32* actualCount)
{

    NSData *inAudioData = (NSData *) clientData;

    size_t dataSize = inAudioData.length;
    size_t bytesToRead = 0;

    if(position < dataSize) {
        size_t bytesAvailable = dataSize - position;
        bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable;

        [inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)];
    } else {
        NSLog(@"data was not read \n");
        bytesToRead = 0;
    }

    if(actualCount)
        *actualCount = bytesToRead;

    return noErr;
}

static SInt64 getSizeProc(void* clientData) {
    NSData *inAudioData = (NSData *) clientData;
    size_t dataSize = inAudioData.length;
    return dataSize;
}


推荐答案

问题在于你'尝试使用ASCII编码从音频字节(MP3帧)创建 CFURLRef 对象。 CFURLCreateWithBytes 用于字节字符串,而不是二进制数据(即 http://www.apple.com 作为 char * )。要完成您想要的操作 AudioFileOpenWithCallbacks ,请将您的 NSData 对象作为refcon传递,并处理您的原始读取/搜索您传入的 NSData 上运行的自定义回调。

The problem is that you're trying to create a CFURLRef object from the audio bytes (MP3 frames) using the ASCII encoding. CFURLCreateWithBytes is meant to be used with byte strings, not binary data (i.e., "http://www.apple.com" as a char *). To accomplish what you want use AudioFileOpenWithCallbacks, pass your NSData object as the refcon, and handle raw reading/seeking in your custom callbacks operating on the NSData that you passed in.

这篇关于从NSData解码MP3文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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