Пример #1
0
/*----------------------------------------------------------------------
|    AlsaOutput_PutPacket
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutput_PutPacket(BLT_PacketConsumer* _self,
                     BLT_MediaPacket*    packet)
{
    AlsaOutput*             self = ATX_SELF(AlsaOutput, BLT_PacketConsumer);
    const BLT_PcmMediaType* media_type;
    BLT_ByteBuffer          buffer;
    BLT_Size                size;
    BLT_Result              result;

    /* check parameters */
    if (packet == NULL) {
        return BLT_ERROR_INVALID_PARAMETERS;
    }

    /* check the payload buffer and size */
    buffer = BLT_MediaPacket_GetPayloadBuffer(packet);
    size = BLT_MediaPacket_GetPayloadSize(packet);
    if (size == 0) return BLT_SUCCESS;

    /* get the media type */
    result = BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)(const void*)&media_type);
    if (BLT_FAILED(result)) return result;

    /* check the media type */
    if (media_type->base.id != BLT_MEDIA_TYPE_ID_AUDIO_PCM) {
        return BLT_ERROR_INVALID_MEDIA_TYPE;
    }

    /* configure the device for this format */
    result = AlsaOutput_Configure(self, media_type);
	if (BLT_FAILED(result)) return result;
	
    /* update the media time */
    {
        BLT_TimeStamp ts = BLT_MediaPacket_GetTimeStamp(packet);
        ATX_UInt64    ts_nanos = BLT_TimeStamp_ToNanos(ts);
        BLT_TimeStamp packet_duration;
        if (media_type->sample_rate   && 
            media_type->channel_count && 
            media_type->bits_per_sample) {
            unsigned int sample_count = BLT_MediaPacket_GetPayloadSize(packet)/
                                        (media_type->channel_count*media_type->bits_per_sample/8);
            packet_duration = BLT_TimeStamp_FromSamples(sample_count, media_type->sample_rate);            
        } else {
            packet_duration = BLT_TimeStamp_FromSeconds(0);
        }
        if (ts_nanos == 0) {
            self->media_time = self->next_media_time;
        } else {
            self->media_time = ts_nanos;
        }
        self->next_media_time = self->media_time+BLT_TimeStamp_ToNanos(packet_duration);
    }
    
    /* write the audio samples */
    return AlsaOutput_Write(self, buffer, size);
}
Пример #2
0
/*----------------------------------------------------------------------
|    AlsaOutput_Seek
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutput_Seek(BLT_MediaNode* _self,
                BLT_SeekMode*  mode,
                BLT_SeekPoint* point)
{
    AlsaOutput* self = ATX_SELF_EX(AlsaOutput, BLT_BaseMediaNode, BLT_MediaNode);
    BLT_COMPILER_UNUSED(mode);
    BLT_COMPILER_UNUSED(point);

    /* ignore unless we're prepared */
    if (self->state != BLT_ALSA_OUTPUT_STATE_PREPARED) {
        return BLT_SUCCESS;
    }

    /* reset the device */
    AlsaOutput_Reset(self);

    /* update the media time */
    if (point->mask & BLT_SEEK_POINT_MASK_TIME_STAMP) {
        self->media_time = BLT_TimeStamp_ToNanos(point->time_stamp);
        self->next_media_time = self->media_time;
    } else {
        self->media_time = 0;
        self->next_media_time = 0;
    }
    
    return BLT_SUCCESS;
}
Пример #3
0
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_GetStatus
+---------------------------------------------------------------------*/
BLT_METHOD
OsxAudioUnitsOutput_GetStatus(BLT_OutputNode*       _self,
                              BLT_OutputNodeStatus* status)
{
    OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_OutputNode);
    
    /* default values */
    status->flags = 0;
    
    pthread_mutex_lock(&self->lock);
    
    /* check if the queue is full */
    if (ATX_List_GetItemCount(self->packet_queue) >= self->max_packets_in_queue) {
        ATX_LOG_FINER("packet queue is full");
        status->flags |= BLT_OUTPUT_NODE_STATUS_QUEUE_FULL;
    }

    /* compute the media time */
    BLT_TimeStamp_Set(status->media_time, 0, 0);
    if (self->media_time_snapshot.rendered_host_time) {
        UInt64 host_time  = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
        UInt64 media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts); 
        ATX_LOG_FINER_3("host time = %lld, last rendered packet = %lld, rendered ts = %lld", 
                        host_time, 
                        self->media_time_snapshot.rendered_host_time,
                        media_time);
        if (host_time > self->media_time_snapshot.rendered_host_time) {
            media_time += host_time-self->media_time_snapshot.rendered_host_time;
        } 
        UInt64 max_media_time;
        max_media_time = BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_packet_ts) +
                         BLT_TimeStamp_ToNanos(self->media_time_snapshot.rendered_duration);
        ATX_LOG_FINER_2("computed media time = %lld, max media time = %lld",
                        media_time, max_media_time);
        if (media_time > max_media_time) {
            ATX_LOG_FINER("media time clamped to max");
            media_time = max_media_time;
        }
        status->media_time = BLT_TimeStamp_FromNanos(media_time);
        ATX_LOG_FINER_1("media time = %lld", media_time);
    }
    
    pthread_mutex_unlock(&self->lock);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|    BLT_DecoderServer::UpdateStatus
+---------------------------------------------------------------------*/
BLT_Result
BLT_DecoderServer::UpdateStatus()
{
    BLT_DecoderStatus status;
    BLT_Result        result;

    // get the decoder status
    result = BLT_Decoder_GetStatus(m_Decoder, &status);
    if (BLT_FAILED(result)) return result;

    // notify if the time has changed by more than the update threshold
    // NOTE: current and previous here are measured in milliseconds
    ATX_UInt64 previous = BLT_TimeStamp_ToNanos(m_DecoderStatus.time_stamp)/1000000;
    ATX_UInt64 current  = BLT_TimeStamp_ToNanos(status.time_stamp)/1000000;
    if (m_TimeStampUpdateQuantum) {
        // make the new time stamp a multiple of the update quantum
        current /= m_TimeStampUpdateQuantum;
        current *= m_TimeStampUpdateQuantum;
    }
    if (current != previous) {
        m_DecoderStatus.time_stamp = BLT_TimeStamp_FromMillis(current);
        NotifyTimeCode();
    }

    // convert the stream position into a decoder position 
    if (m_PositionUpdateRange != 0) {
        ATX_UInt64 ratio = status.position.range/m_PositionUpdateRange;
        ATX_UInt64 offset;
        if (ratio == 0) {
            offset = 0;
        } else {
            offset = status.position.offset/ratio;
        }
        if (offset != m_DecoderStatus.position.offset) {
            m_DecoderStatus.position.offset = offset;
            NotifyPosition();
        }
    }

    return BLT_SUCCESS;
}
Пример #5
0
/*----------------------------------------------------------------------
|    AlsaOutput_GetStatus
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutput_GetStatus(BLT_OutputNode*       _self,
                     BLT_OutputNodeStatus* status)
{
    AlsaOutput*       self = ATX_SELF(AlsaOutput, BLT_OutputNode);
    snd_pcm_status_t* pcm_status;
    snd_pcm_sframes_t delay = 0;
    int               io_result;

    /* default values */
    status->media_time.seconds = 0;
    status->media_time.nanoseconds = 0;
    status->flags = 0;

    /* get the driver status */
    snd_pcm_status_alloca_no_assert(&pcm_status);
    io_result = snd_pcm_status(self->device_handle, pcm_status);
    if (io_result != 0) {
        return BLT_FAILURE;
    }
    delay = snd_pcm_status_get_delay(pcm_status);
    if (delay == 0) {
        /* workaround buggy alsa drivers */
        io_result = snd_pcm_delay(self->device_handle, &delay);
        if (io_result != 0) {
            return BLT_FAILURE;
        }
    }
    
    if (delay > 0 && self->media_type.sample_rate) {
        ATX_UInt64 media_time_samples = (self->next_media_time * 
                                         (ATX_UInt64)self->media_type.sample_rate)/
                                         (ATX_UInt64)1000000000;
        ATX_UInt64 media_time_ns;
        if (delay <= (snd_pcm_sframes_t)media_time_samples) {
            media_time_samples -= delay;
        } else {
            media_time_samples = 0;
        }
        media_time_ns = (media_time_samples*(ATX_UInt64)1000000000)/self->media_type.sample_rate;
        status->media_time = BLT_TimeStamp_FromNanos(media_time_ns);
    } else {
        status->media_time = BLT_TimeStamp_FromNanos(self->next_media_time);
    }
    
    /* return the computed media time */
    ATX_LOG_FINEST_3("delay = %lld samples, input port time = %lld, media time = %lld", (ATX_UInt64)delay, self->next_media_time, BLT_TimeStamp_ToNanos(status->media_time));
    return BLT_SUCCESS;
}
Пример #6
0
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_RenderCallback
+---------------------------------------------------------------------*/
static OSStatus     
OsxAudioUnitsOutput_RenderCallback(void*						inRefCon,
                                   AudioUnitRenderActionFlags*	ioActionFlags,
                                   const AudioTimeStamp*		inTimeStamp,
                                   UInt32						inBusNumber,
                                   UInt32						inNumberFrames,
                                   AudioBufferList*			    ioData)
{
    OsxAudioUnitsOutput* self = (OsxAudioUnitsOutput*)inRefCon;
    ATX_ListItem*        item;
    unsigned int         requested;
    unsigned char*       out;
    ATX_Boolean          timestamp_measured = ATX_FALSE;
    
    BLT_COMPILER_UNUSED(ioActionFlags);
    BLT_COMPILER_UNUSED(inTimeStamp);
    BLT_COMPILER_UNUSED(inBusNumber);
    BLT_COMPILER_UNUSED(inNumberFrames);
                
    /* sanity check on the parameters */
    if (ioData == NULL || ioData->mNumberBuffers == 0) return 0;
    
    /* in case we have a strange request with more than one buffer, just return silence */
    if (ioData->mNumberBuffers != 1) {
        unsigned int i;
        ATX_LOG_FINEST_1("strange request with %d buffers", 
                         (int)ioData->mNumberBuffers);
        for (i=0; i<ioData->mNumberBuffers; i++) {
            ATX_SetMemory(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
        }
        return 0;
    }
        
    /* init local variables */
    requested = ioData->mBuffers[0].mDataByteSize;
    out = (unsigned char*)ioData->mBuffers[0].mData;
    ATX_LOG_FINEST_2("request for %d bytes, %d frames", (int)requested, (int)inNumberFrames);

    /* lock the packet queue */
    pthread_mutex_lock(&self->lock);
    
    /* return now if we're paused */
    //if (self->paused) goto end;
    
    /* abort early if we have no packets */
    if (ATX_List_GetItemCount(self->packet_queue) == 0) goto end;
        
    /* fill as much as we can */
    while (requested && (item = ATX_List_GetFirstItem(self->packet_queue))) {
        BLT_MediaPacket*        packet = ATX_ListItem_GetData(item);
        const BLT_PcmMediaType* media_type;
        BLT_Size                payload_size;
        BLT_Size                chunk_size;
        BLT_TimeStamp           chunk_duration;
        BLT_TimeStamp           packet_ts;
        unsigned int            bytes_per_frame;
        unsigned int            sample_rate;
        
        /* get the packet info */
        BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&media_type);
        packet_ts = BLT_MediaPacket_GetTimeStamp(packet);
        bytes_per_frame = media_type->channel_count*media_type->bits_per_sample/8;
        sample_rate = media_type->sample_rate;
        
        /* record the timestamp if we have not already done so */
        if (!timestamp_measured) {
            self->media_time_snapshot.rendered_packet_ts = packet_ts;
            self->media_time_snapshot.rendered_host_time = 
                AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
            BLT_TimeStamp_Set(self->media_time_snapshot.rendered_duration, 0, 0);
            timestamp_measured = ATX_TRUE;
            
            ATX_LOG_FINEST_2("rendered TS: packet ts=%lld, host ts=%lld",
                             BLT_TimeStamp_ToNanos(packet_ts),
                             self->media_time_snapshot.rendered_host_time);
        }
         
        /* compute how much to copy from this packet */
        payload_size = BLT_MediaPacket_GetPayloadSize(packet);
        if (payload_size <= requested) {
            /* copy the entire payload and remove the packet from the queue */
            chunk_size = payload_size;
            ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size);
            ATX_List_RemoveItem(self->packet_queue, item);
            packet = NULL;
            media_type = NULL;
            ATX_LOG_FINER_1("media packet fully consumed, %d left in queue",
                            ATX_List_GetItemCount(self->packet_queue));
        } else {
            /* only copy a portion of the payload */
            chunk_size = requested;
            ATX_CopyMemory(out, BLT_MediaPacket_GetPayloadBuffer(packet), chunk_size);            
        }
        
        /* update the counters */
        requested -= chunk_size;
        out       += chunk_size;
        
        /* update the media time snapshot */
        if (bytes_per_frame) {
            unsigned int frames_in_chunk = chunk_size/bytes_per_frame;
            chunk_duration = BLT_TimeStamp_FromSamples(frames_in_chunk, sample_rate);
        } else {
            BLT_TimeStamp_Set(chunk_duration, 0, 0);
        }
        self->media_time_snapshot.rendered_duration = 
            BLT_TimeStamp_Add(self->media_time_snapshot.rendered_duration, chunk_duration);
        
        /* update the packet unless we're done with it */
        if (packet) {
            /* update the packet offset and timestamp */
            BLT_MediaPacket_SetPayloadOffset(packet, BLT_MediaPacket_GetPayloadOffset(packet)+chunk_size);
            BLT_MediaPacket_SetTimeStamp(packet, BLT_TimeStamp_Add(packet_ts, chunk_duration));
        }
    }
   
end:
    /* fill whatever is left with silence */    
    if (requested) {
        ATX_LOG_FINEST_1("filling with %d bytes of silence", requested);
        ATX_SetMemory(out, 0, requested);
    }
    
    pthread_mutex_unlock(&self->lock);
        
    return 0;
}