.m4a raw data from iPod Library not playing

1.6k views Asked by At

So I am faced with a very weird and strange problem and was wondering if anyone else has come across this issue. I am grabbing the raw data from MPMediaItem from the phones music library and then sending it out via HTTP to be played elsewhere. Where my issue is arising is when I am grabbing the raw data from a file of type .m4a it seems to be missing pieces. For example if the original file that I check from itunes is 7.4mb what ill get from my code is of size 7.3mb. Ive done some research and found that a .m4a file is actually an encapsulation and I think I am not getting the encapsulation of the file just the raw music data so therefor it is non recognizable. Here is my code that gives me the raw music data from the MPMediaItem

                    NSError * error = nil;
                    MPMediaQuery *query = [MPMediaQuery albumsQuery];
                    NSArray * songs = query.items;  
                    MPMediaItem * song = [songs objectAtIndex:socket_data_index];  
                    AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:[song valueForProperty:MPMediaItemPropertyAssetURL] options:nil];
                    AVAssetReader * reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error];                  
                    AVAssetTrack * songTrack = [songAsset.tracks objectAtIndex:0];
                    AVAssetReaderTrackOutput * output = [[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:nil];
                    [reader addOutput:output];                         
                    [output release]; 



                    [reader startReading];

                    while (reader.status == AVAssetReaderStatusReading)
                    {                            
                        AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[reader.outputs objectAtIndex:0];
                        CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer];                            
                        if (sampleBufferRef)
                        {
                            CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef);

                            size_t length = CMBlockBufferGetDataLength(blockBufferRef);
                            NSMutableData * data = [[NSMutableData alloc] initWithLength:length];
                            CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, data.mutableBytes);

                            [data_to_return appendData:data];
                            [data release];

                            CMSampleBufferInvalidate(sampleBufferRef);
                            CFRelease(sampleBufferRef);
                        }
                    }

                    if (reader.status == AVAssetReaderStatusFailed || reader.status == AVAssetReaderStatusUnknown)
                    {
                        // Something went wrong. Handle it.
                    }

                    if (reader.status == AVAssetReaderStatusCompleted)
                    {                         

                       return [[[HTTPDataResponse alloc] initWithData:data_to_return] autorelease];
                    }

                    [reader release];

I do and will get the correct data for .mp3 files that are in the phones library but when it comes to .m4a it seems to be missing some parts.

Thanks again for taking your time to help me out.

1

There are 1 answers

3
melle On

I ran into the same problem. AVAssetReader returns only the raw audio data. It works for .mp3 because the file format is just a bunch of frames that contain everything a player needs to playback the file. You may note that all of the id3 information is missing in the exported file.

.m4a audio files are containers that wrap around aac audio data. You received only the audio data, without further information i.e. samplerate or the seek-table.

A workaround for your problem could be to use an AVAssetExportSession to export the AVAssetTrack to a file on the phone and stream the file from there. I tried exactly this and a working .m4a file was written.

The following code is based on Apples example how to export a trimmed audio asset:

// create the export session
AVAssetExportSession *exportSession = [AVAssetExportSession
                                       exportSessionWithAsset: songAsset
                                       presetName:AVAssetExportPresetAppleM4A];
if (nil == exportSession)
    NSLog(@"Error");

// configure export session  output with all our parameters
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
NSString *filePath = [NSString stringWithFormat: @"%@/export.m4a", basePath];

exportSession.outputURL = [NSURL fileURLWithPath: filePath]; // output path
exportSession.outputFileType = AVFileTypeAppleM4A; // output file type

[exportSession exportAsynchronouslyWithCompletionHandler:^{
    if (AVAssetExportSessionStatusCompleted == exportSession.status)
    {
        NSLog(@"AVAssetExportSessionStatusCompleted");
    }
    else if (AVAssetExportSessionStatusFailed == exportSession.status)
    {
        // a failure may happen because of an event out of your control
        // for example, an interruption like a phone call comming in
        // make sure and handle this case appropriately
        NSLog(@"AVAssetExportSessionStatusFailed");
    }
    else
    {
        NSLog(@"Export Session Status: %d", exportSession.status);
    }
}];

The downside of this solution is, that the while file had to be written on the disk. I'm still searching for a better way to handle .m4a files.