fabrizotus
fabrizotus

Reputation: 2978

glitches when playing audio in pieces with AVAudioPLayer

I'm trying to play audio from an IP camera streaming with it is API http://...<ip-camera..>/audio.cgi

Currently i obtain to play stream audio with adapting a MJPEG client, taking audio blocks and put them to a buffer (NSMutableArray) of consecutive sounds packages. Here some code which receives the data:

-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"Data recv.");

     NSLog(@"Data lenght:%i",[data length]);

    if ([data length]>AUDIO_STREAM_HEADER_SIZE){
        [recvData appendData:data]; //audio stream data - always 1024 bytes
        NSLog(@"Data Append lenght:%i",[recvData length]);

    }
    else {
        [_encabezado appendData:data]; //audio stream header - always 44 bytes
        NSLog(@"Crea encabezado:%@",_encabezado);
        NSLog(@"Largo encabezado:%i",[_encabezado length]);

    }


    if ([recvData length] >= PACKET_SIZE_BUFFER_SONIDO) //PACKET_SIZE_BUFFER_SONIDO = 81920
    {

        NSMutableData *soundData = [[NSMutableData alloc] initWithCapacity:([recvData length])];

        NSMutableData *soundCut = [[NSMutableData alloc] initWithData:recvData];

        [soundData appendData:_encabezado]; //audio stream header
        [soundData appendData:soundCut];  //audio stream data

        [_arrSonidos addObject:soundData];

        if ([_arrSonidos count]>(BUFFER_SIZE_FOR_PLAY-1)) {
            if (playerBuffer.isPlaying) {
                [playerBuffer AgregaDataSound:soundData]; //addDataSound in Buffer
            } else {
                playerBuffer = [[SoundDataPLayer alloc]initWithFileNameQueue:_arrSonidos];
            }
        }

        [recvData setLength:0];

        [soundData release];
    }

}

Each time when the function is called (function show up), the header of data field is getting (44 bytes) and then bits of data bytes (1024 bytes) once per call. what I do is keep the head receiving data in a NSData and then receiving data until a defined packet size, keeping the package in the buffer with their respective header data. And so I iterated adding packages to the buffer.

And the other hand, i create a "playerBuffer" (inspired by examples on the web) called by the above code and is also responsible for controlling the buffer and playing every packet of the buffer. This object is based on a AVAudioPlayer:

SoundDataPLayer.h

#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface SoundDataPLayer : NSObject <AVAudioPlayerDelegate> {
    AVAudioPlayer* myplayer;
    NSMutableArray* dataSound;
    int index;
}

@property (nonatomic, retain) AVAudioPlayer* myplayer;
@property (nonatomic, retain) NSMutableArray* dataSound;

- (id)initWithFileNameQueue:(NSArray*)datas;
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
- (void)playX:(int)i;
- (void)stop;
- (BOOL)isPlaying;
- (void)AgregaDataSound:(NSData *)data;

@end

SoundDataPLayer.m

#import "SoundDataPLayer.h"
#import "ExtAudioFileConvertUtil.h"

@implementation SoundDataPLayer

@synthesize myplayer;
@synthesize dataSound;

- (id)initWithFileNameQueue:(NSArray*)datas {
    if ((self = [super init])) {
        self.dataSound = [[NSMutableArray alloc]initWithArray:datas];
        index = 0;
        [self playX:index];
    }
    return self;
}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
    if (index < dataSound.count) {
        [self playX:index];
    } else {
        //reached end of queue
    }
}

- (void)playX:(int)i {

    self.myplayer = [[AVAudioPlayer alloc] initWithData:[dataSound objectAtIndex:i]  error:nil];
    myplayer.delegate = self;
    [myplayer prepareToPlay];
    [myplayer play];

    index++;
}

- (void)stop {
    if (self.myplayer.playing) [myplayer stop];
}

- (BOOL)isPlaying{
    return self.myplayer.playing;
}

- (void)AgregaDataSound:(NSData *)data {
    [dataSound addObject:data]; //Add Sound Data to the buffer
}

@end

That is work and play looped the audio, but on each loop when the AVAudioPlayer back to play a new packet (NSData) of buffer, it hear an annoying glitches between each packet.

I tried with SystemSoundand recording sound packs buffer into data file to see if the data containing the glitches, but apparently the fact that glitches is in the end of data.

I found an article in a forum that seems to explain what happens: here but apparently does not fully explain in my case because is a x-wav audio and PCM codec, and so I think and understand is uncompressed. Although I do not know how you uncompress the audio from NSData packets.

Additional information of streaming audio from the IP camera:

datos del audio en el header:

"Cache-Control" = "no-cache"; "Content-Type" = "audio/x-wav"; Date = "Tue Jan 5 01:17:04 2010"; Pragma = "no-cache"; Server = "GoAhead-Webs";

Datos adicionales del audio Codec: PCM SampleRate: 16 kHz

I would appreciate if anyone could give me a light on how to solve this problem... I try to solve it many many hours... is driving me crazy!!

Thanks in advance and sorry for my bad english.

Upvotes: 1

Views: 1148

Answers (2)

fabrizotus
fabrizotus

Reputation: 2978

I resolve my problem with AVQueuePlayer.

I hope it helps someone.

Upvotes: 1

tc.
tc.

Reputation: 33592

The "glitch" you're hearing is the combination of the time it takes you to receive the "did finish playing" event, process it, initialize the AVAudioPlayer for the next data packet, and send the data to the sound card.

It's analogus to waiting for one CD player to finish playing before starting the next — it's impossible to react fast enough for glitch-free playback.

You probably want to use Audio Queue services, and you'll also want some way to compensate for audio clock skew between the camera and phone.

Upvotes: 1

Related Questions