为ffmpeg rtsp流播放设置音频队列

时间:2011-11-19 10:59:04

标签: ios ffmpeg rtsp

我正在使用ffmpeg为iOS创建一个rtsp流媒体(AAC格式)客户端。现在我只能说我的应用程序是可行的,但是流声音非常嘈杂,甚至有点失真,比vlc或mplayer播放时更糟糕。

流由av_read_frame()读取,由avcodec_decode_audio3()解码。然后我将解码后的原始音频发送到音频队列。

使用我的应用程序解码本地aac文件时,声音似乎不那么嘈杂。我知道初始编码会显着影响结果。但至少我应该尝试让它听起来像其他流媒体客户端......

我的实现/修改中的许多部分实际上来自try和error。我相信我在设置音频队列方面做错了,还有用于填充音频缓冲区的回调函数。

非常感谢任何提示,建议或帮助。

// - 由av_dump_format() -

转储的测试材料的信息
Metadata:
    title           : /demo/test.3gp
  Duration: 00:00:30.11, start: 0.000000, bitrate: N/A
    Stream #0:0: Audio: aac, 32000 Hz, stereo, s16
aac  Advanced Audio Coding 

// - 音频队列设置程序 -

- (void) startPlayback
{
    OSStatus err = 0;
    if(playState.playing) return;

    playState.started = false;

    if(!playState.queue) 
    {

        UInt32 bufferSize;


        playState.format.mSampleRate = _av->audio.sample_rate;
        playState.format.mFormatID = kAudioFormatLinearPCM;
        playState.format.mFormatFlags = kAudioFormatFlagsCanonical;
        playState.format.mChannelsPerFrame = _av->audio.channels_per_frame;
        playState.format.mBytesPerPacket = sizeof(AudioSampleType) *_av->audio.channels_per_frame;
        playState.format.mBytesPerFrame = sizeof(AudioSampleType) *_av->audio.channels_per_frame;
        playState.format.mBitsPerChannel = 8 * sizeof(AudioSampleType);

        playState.format.mFramesPerPacket = 1;        
        playState.format.mReserved = 0;


        pauseStart = 0;
        DeriveBufferSize(playState.format,playState.format.mBytesPerPacket,BUFFER_DURATION,&bufferSize,&numPacketsToRead);
        err= AudioQueueNewOutput(&playState.format, aqCallback, &playState, NULL, kCFRunLoopCommonModes, 0, &playState.queue);

        if(err != 0)
        {
            printf("AQHandler.m startPlayback: Error creating new AudioQueue: %d \n", (int)err);
        }

        for(int i = 0 ; i < NUM_BUFFERS ; i ++)
        {
            err = AudioQueueAllocateBufferWithPacketDescriptions(playState.queue, bufferSize, numPacketsToRead , &playState.buffers[i]);

            if(err != 0)
                printf("AQHandler.m startPlayback: Error allocating buffer %d", i);
            fillAudioBuffer(&playState,playState.queue, playState.buffers[i]);
        }

    }

    startTime = mu_currentTimeInMicros();

    err=AudioQueueStart(playState.queue, NULL);

    if(err)
    {

        char sErr[4];
        printf("AQHandler.m startPlayback: Could not start queue %ld %s.", err, FormatError(sErr,err));

        playState.playing = NO;
    } 
    else
    {
        AudioSessionSetActive(true);
        playState.playing = YES;
    }           
}

// - 用于填充音频缓冲区的回调 -

static int ct = 0;
static void fillAudioBuffer(void *info,AudioQueueRef queue, AudioQueueBufferRef buffer)
{

    int lengthCopied = INT32_MAX;
    int dts= 0;
    int isDone = 0;

    buffer->mAudioDataByteSize = 0;
    buffer->mPacketDescriptionCount = 0;

    OSStatus err = 0;
    AudioTimeStamp bufferStartTime;

    AudioQueueGetCurrentTime(queue, NULL, &bufferStartTime, NULL);


    PlayState *ps = (PlayState *)info;

    if (!ps->started)
        ps->started = true;

    while(buffer->mPacketDescriptionCount < numPacketsToRead && lengthCopied > 0)
    {
        lengthCopied = getNextAudio(_av,
                        buffer->mAudioDataBytesCapacity-buffer->mAudioDataByteSize,
                        (uint8_t*)buffer->mAudioData+buffer->mAudioDataByteSize,
                        &dts,&isDone);

        ct+= lengthCopied;

        if(lengthCopied < 0 || isDone) 
        {
            printf("nothing to read....\n\n");
            PlayState *ps = (PlayState *)info;
            ps->finished = true;
            ps->started = false;
            break;
        }

        if(aqStartDts < 0) aqStartDts = dts;

        if(buffer->mPacketDescriptionCount ==0)
        {
            bufferStartTime.mFlags = kAudioTimeStampSampleTimeValid;
            bufferStartTime.mSampleTime = (Float64)(dts-aqStartDts);//* _av->audio.frame_size;

            if (bufferStartTime.mSampleTime <0 ) 
                bufferStartTime.mSampleTime = 0;

            printf("AQHandler.m fillAudioBuffer: DTS for %x: %lf time base: %lf StartDTS: %d\n", 
                    (unsigned int)buffer, 
                    bufferStartTime.mSampleTime, 
                    _av->audio.time_base, 
                    aqStartDts);

        }

        buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mStartOffset = buffer->mAudioDataByteSize;
        buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mDataByteSize = lengthCopied;



        buffer->mPacketDescriptions[buffer->mPacketDescriptionCount].mVariableFramesInPacket = 0;

        buffer->mPacketDescriptionCount++;

        buffer->mAudioDataByteSize += lengthCopied;
    }

    int audioBufferCount, audioBufferTotal,  videoBufferCount, videoBufferTotal;
    bufferCheck(_av,&videoBufferCount, &videoBufferTotal, &audioBufferCount, &audioBufferTotal);

    if(buffer->mAudioDataByteSize)
    {

        err = AudioQueueEnqueueBufferWithParameters(queue, buffer, 0, NULL, 0, 0, 0, NULL, &bufferStartTime, NULL);

        if(err)
        {
            char sErr[10];
            printf("AQHandler.m fillAudioBuffer: Could not enqueue buffer 0x%x: %d %s.", buffer, err, FormatError(sErr, err));

        }

    }

}




int getNextAudio(video_data_t* vInst, int maxlength, uint8_t* buf, int* pts, int* isDone) 
{

    struct video_context_t  *ctx = vInst->context;
    int    datalength            = 0;

    while(ctx->audio_ring.lock || (ctx->audio_ring.count <= 0 && ((ctx->play_state & STATE_DIE) != STATE_DIE)))
    {

        if (ctx->play_state & STATE_EOF) return -1;        
        usleep(100);
    }

    *pts = 0;
    ctx->audio_ring.lock = kLocked;

    if(ctx->audio_ring.count>0 && maxlength > ctx->audio_buffer[ctx->audio_ring.read].size)
    {    
        memcpy(buf, ctx->audio_buffer[ctx->audio_ring.read].data,ctx->audio_buffer[ctx->audio_ring.read].size);

        *pts = ctx->audio_buffer[ctx->audio_ring.read].pts;

        datalength = ctx->audio_buffer[ctx->audio_ring.read].size;

        ctx->audio_ring.read++;        
        ctx->audio_ring.read %= ABUF_SIZE;        
        ctx->audio_ring.count--;

    }
    ctx->audio_ring.lock = kUnlocked;

    if((ctx->play_state & STATE_EOF) == STATE_EOF && ctx->audio_ring.count == 0) *isDone = 1;

    return datalength;
}

2 个答案:

答案 0 :(得分:1)

如果要在音频队列回调中下载或解码音频,NUM_BUFFERS和/或bufferSize可能需要更大才能覆盖更糟糕的网络延迟和抖动。或者您可以在音频回调之外预解码音频,并在回调之前排队足够的数据以处理下载和解码时间和方差。

答案 1 :(得分:1)

声音失真的最可能原因是简单的数据包丢失,RTSP很容易受到影响,特别是在无线连接上。

我建议你考虑配置ffmpeg以尽可能使用基于TCP的连接而不是默认的RTP / UDP。