/*----------------------------------------------------------------------
|    DebugOutput_PutPacket
+---------------------------------------------------------------------*/
BLT_METHOD
DebugOutput_PutPacket(BLT_PacketConsumer* _self,
                      BLT_MediaPacket*    packet)
{
    DebugOutput*         self = ATX_SELF(DebugOutput, BLT_PacketConsumer);
    const BLT_MediaType* media_type;

    /* check the media type */
    BLT_MediaPacket_GetMediaType(packet, &media_type);
    if (self->expected_media_type->id != BLT_MEDIA_TYPE_ID_UNKNOWN &&
        self->expected_media_type->id != media_type->id) {
        return BLT_ERROR_INVALID_MEDIA_TYPE;
    }

#if defined(ATX_CONFIG_ENABLE_LOGGING)
    /* print type info extensions if they are known to us */
    if (media_type->id == BLT_MEDIA_TYPE_ID_AUDIO_PCM) {
        BLT_PcmMediaType* pcm_type = (BLT_PcmMediaType*)media_type;
        ATX_LOG_INFO_3("PCM packet - sr=%ld, ch=%d, bps=%d",
                       pcm_type->sample_rate,
                       pcm_type->channel_count,
                       pcm_type->bits_per_sample);
    } else {
        ATX_LOG_INFO_1("packet - type=%d", media_type->id);
    }
#endif

    return BLT_SUCCESS;
}
Exemple #2
0
/*----------------------------------------------------------------------
|       StdcFileOutputStream_Tell
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileOutputStream_Tell(ATX_OutputStream* _self, 
                          ATX_Offset*       where)
{
    return StdcFileStream_Tell(ATX_SELF(StdcFileStream, ATX_OutputStream), 
                               where);
}
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_Drain
+---------------------------------------------------------------------*/
BLT_METHOD
OsxAudioUnitsOutput_Drain(BLT_OutputNode* _self)
{
    OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_OutputNode);
    unsigned int watchdog = 20000000/BLT_OSX_AUDIO_UNITS_OUTPUT_SLEEP_INTERVAL;
    
    ATX_LOG_FINER("draining packets"); 

    /* lock the queue */
    pthread_mutex_lock(&self->lock);
    
    /* wait until there are no more packets in the queue */
    while (ATX_List_GetItemCount(self->packet_queue)) {
        pthread_mutex_unlock(&self->lock);
        ATX_LOG_FINER("waiting..."); 
        usleep(BLT_OSX_AUDIO_UNITS_OUTPUT_SLEEP_INTERVAL);
        pthread_mutex_lock(&self->lock);
        
        if (--watchdog == 0) {
            ATX_LOG_WARNING("*** the watchdog bit us ***");
            break;
        }
    }

    /* unlock the queue */
    pthread_mutex_unlock(&self->lock);
    
    ATX_LOG_FINER("end"); 

    return BLT_SUCCESS;
}
Exemple #4
0
/*----------------------------------------------------------------------
|       StdcFileInputStream_Seek
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileInputStream_Seek(ATX_InputStream* _self, 
                         ATX_Offset       where)
{
    return StdcFileStream_Seek(ATX_SELF(StdcFileStream, ATX_InputStream), 
                               where);
}
Exemple #5
0
/*----------------------------------------------------------------------
|       StdcFileInputStream_Read
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileInputStream_Read(ATX_InputStream* _self,
                         ATX_Any          buffer, 
                         ATX_Size         bytes_to_read, 
                         ATX_Size*        bytes_read)
{
    StdcFileStream* self = ATX_SELF(StdcFileStream, ATX_InputStream);
    size_t          nb_read;

    nb_read = fread(buffer, 1, (size_t)bytes_to_read, self->file->file);
    if (nb_read > 0 || bytes_to_read == 0) {
        if (bytes_read) *bytes_read = nb_read;
        self->file->position += nb_read;
        return ATX_SUCCESS;
    } else {
        if (bytes_read) *bytes_read = 0;
        if (nb_read == 0 || feof(self->file->file) != 0) {
            return ATX_ERROR_EOS;
        } else {
            return ATX_FAILURE;
        }
    }

    return ATX_SUCCESS;
}
Exemple #6
0
/*----------------------------------------------------------------------
|       StdcFileInputStream_GetAvailable
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileInputStream_GetAvailable(ATX_InputStream* _self, 
                                 ATX_Size*        size)
{
    StdcFileStream* self = ATX_SELF(StdcFileStream, ATX_InputStream);
    *size = self->file->size - self->file->position;
    return ATX_SUCCESS;
}
Exemple #7
0
/*----------------------------------------------------------------------
|       StdcFileInputStream_GetSize
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileInputStream_GetSize(ATX_InputStream* _self, 
                            ATX_Size*        size)
{
    StdcFileStream* self = ATX_SELF(StdcFileStream, ATX_InputStream);
    *size = self->file->size;
    return ATX_SUCCESS;
}
/*----------------------------------------------------------------------
|    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);
}
Exemple #9
0
/*----------------------------------------------------------------------
|       Listener_Destroy
+---------------------------------------------------------------------*/
ATX_METHOD
Listener_Destroy(ATX_Destroyable* _self)
{
    Listener* self = ATX_SELF(Listener, ATX_Destroyable);
    ATX_String_Destruct(&self->name);
    ATX_FreeMemory((void*)self);

    return ATX_SUCCESS;
}
/*----------------------------------------------------------------------
|    BLT_DecoderServer_PropertyListenerWrapper_OnPropertyChanged
+---------------------------------------------------------------------*/
BLT_VOID_METHOD
BLT_DecoderServer_PropertyListenerWrapper_OnPropertyChanged(
    ATX_PropertyListener*    _self,
    ATX_CString              name,
    const ATX_PropertyValue* value)    
{
    BLT_DecoderServer_PropertyListenerWrapper* self = ATX_SELF(BLT_DecoderServer_PropertyListenerWrapper, ATX_PropertyListener);
    self->outer->OnPropertyChanged(self->scope, self->source, name, value);
}
/*----------------------------------------------------------------------
|   BLT_DecoderServer_EventListenerWrapper_OnEvent
+---------------------------------------------------------------------*/
BLT_VOID_METHOD 
BLT_DecoderServer_EventListenerWrapper_OnEvent(
    BLT_EventListener* _self,
    ATX_Object*        source,
    BLT_EventType      type,
    const BLT_Event*   event)
{
    BLT_DecoderServer_EventListenerWrapper* self = ATX_SELF(BLT_DecoderServer_EventListenerWrapper, BLT_EventListener);
    self->outer->OnEvent(source, type, event);
}
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_PutPacket
+---------------------------------------------------------------------*/
BLT_METHOD
OsxAudioUnitsOutput_PutPacket(BLT_PacketConsumer* _self,
                              BLT_MediaPacket*    packet)
{
    OsxAudioUnitsOutput*    self = ATX_SELF(OsxAudioUnitsOutput, BLT_PacketConsumer);
    const BLT_PcmMediaType* media_type;
    BLT_Result              result;

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

    /* get the media type */
    result = BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&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;
    }

    /* compare the media format with the current format */
    if (media_type->sample_rate     != self->media_type.sample_rate   ||
        media_type->channel_count   != self->media_type.channel_count ||
        media_type->bits_per_sample != self->media_type.bits_per_sample) {
        /* new format */
        
        /* check the format */
        if (media_type->sample_rate     == 0 ||
            media_type->channel_count   == 0 ||
            media_type->bits_per_sample == 0) {
            return BLT_ERROR_INVALID_MEDIA_TYPE;
        }

        /* check for the supported sample widths */
        if (media_type->bits_per_sample !=  8 &&
            media_type->bits_per_sample != 16 &&
            media_type->bits_per_sample != 24 &&
            media_type->bits_per_sample != 32) {
            return BLT_ERROR_INVALID_MEDIA_TYPE;
        }
                        
        /* update the audio unit */
        result = OsxAudioUnitsOutput_SetStreamFormat(self, media_type);
        if (BLT_FAILED(result)) return result;
    }
    
    /* queue the packet */
    result = OsxAudioUnitsOutput_QueuePacket(self, packet);
    if (BLT_FAILED(result)) return result;
    
    /* ensure we're not paused */
    return OsxAudioUnitsOutput_Resume(&ATX_BASE_EX(self, BLT_BaseMediaNode, BLT_MediaNode));
}
Exemple #13
0
/*----------------------------------------------------------------------
|       StdcFile_Close
+---------------------------------------------------------------------*/
static ATX_Result
StdcFile_Close(ATX_File* _self)
{
    StdcFile* self = ATX_SELF(StdcFile, ATX_File);

    /* release the resources and reset */
    StdcFileWrapper_Release(self->file);
    self->file = NULL;
    self->mode = 0;

    return ATX_SUCCESS;
}
/*----------------------------------------------------------------------
|    SdlVideoOutput_GetStatus
+---------------------------------------------------------------------*/
BLT_METHOD
SdlVideoOutput_GetStatus(BLT_OutputNode*       _self,
                         BLT_OutputNodeStatus* status)
{
    SdlVideoOutput* self = ATX_SELF(SdlVideoOutput, BLT_OutputNode);
    BLT_COMPILER_UNUSED(self);
    
    /* default value */
    status->media_time.seconds     = 0;
    status->media_time.nanoseconds = 0;

    return BLT_SUCCESS;
}
Exemple #15
0
/*----------------------------------------------------------------------
|   BLT_BaseModule_Attach
+---------------------------------------------------------------------*/
BLT_DIRECT_METHOD
BLT_BaseModule_Attach(BLT_Module* _self, BLT_Core* core)
{
    BLT_COMPILER_UNUSED(_self);
    BLT_COMPILER_UNUSED(core);
#if defined(BLT_DEBUG)
    {
        BLT_BaseModule* self = ATX_SELF(BLT_BaseModule, BLT_Module);
        ATX_LOG_FINE_1("attaching module name=%s", self->info.name?self->info.name:"");
    }
#endif
    return BLT_SUCCESS;
}
Exemple #16
0
/*----------------------------------------------------------------------
|   BLT_BaseModule_GetInfo
+---------------------------------------------------------------------*/
BLT_DIRECT_METHOD
BLT_BaseModule_GetInfo(BLT_Module* _self, BLT_ModuleInfo* info)
{
    BLT_BaseModule* self = ATX_SELF(BLT_BaseModule, BLT_Module);

    /* check parameters */
    if (info == NULL) return BLT_ERROR_INVALID_PARAMETERS;

    /* return the module info */
    *info = self->info;

    return BLT_SUCCESS;
}
Exemple #17
0
/*----------------------------------------------------------------------
|       StdcFile_GetSize
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFile_GetSize(ATX_File* _self, ATX_Size* size)
{
    StdcFile* self = ATX_SELF(StdcFile, ATX_File);

    /* check that the file is open */
    if (self->file == NULL) return ATX_ERROR_FILE_NOT_OPEN;

    /* return the size */
    if (size) *size = self->size;

    return ATX_SUCCESS;
}
Exemple #18
0
/*----------------------------------------------------------------------
|       StdcFile_Destroy
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFile_Destroy(ATX_Destroyable* _self)
{
    StdcFile* self = ATX_SELF(StdcFile, ATX_Destroyable);

    /* release the resources */
    ATX_String_Destruct(&self->name);
    StdcFileWrapper_Release(self->file);

    /* free the memory */
    ATX_FreeMemory((void*)self);

    return ATX_SUCCESS;
}
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_SetVolume
+---------------------------------------------------------------------*/
BLT_METHOD
OsxAudioUnitsOutput_SetVolume(BLT_VolumeControl* _self, float volume)
{
    OsxAudioUnitsOutput* self = ATX_SELF(OsxAudioUnitsOutput, BLT_VolumeControl);
    ComponentResult      result;
    Float32              au_volume = volume;
    
    result = AudioUnitSetParameter(self->audio_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, au_volume, 0);
    if (result == noErr) {
        return BLT_SUCCESS;
    } else {
        return BLT_FAILURE;
    }    
}
/*----------------------------------------------------------------------
|    FileOutput_QueryMediaType
+---------------------------------------------------------------------*/
BLT_METHOD
FileOutput_QueryMediaType(BLT_MediaPort*        _self,
                          BLT_Ordinal           index,
                          const BLT_MediaType** media_type)
{
    FileOutput* self = ATX_SELF(FileOutput, BLT_MediaPort);
    if (index == 0) {
        *media_type = self->media_type;
        return BLT_SUCCESS;
    } else {
        *media_type = NULL;
        return BLT_FAILURE;
    }
}
/*----------------------------------------------------------------------
|    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;
}
/*----------------------------------------------------------------------
|   DcfParserOutput_GetStream
+---------------------------------------------------------------------*/
BLT_METHOD
DcfParserOutput_GetStream(BLT_InputStreamProvider* _self,
                          ATX_InputStream**        stream)
{
    DcfParserOutput* self = ATX_SELF(DcfParserOutput, BLT_InputStreamProvider);

    // copy our stream pointer
    *stream = self->stream;
    if (self->stream == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT;

    // give a reference count to the caller
    ATX_REFERENCE_OBJECT(*stream);
    
    return BLT_SUCCESS;
}
Exemple #23
0
/*----------------------------------------------------------------------
|       Listener_OnPropertyChanged
+---------------------------------------------------------------------*/
static void
Listener_OnPropertyChanged(ATX_PropertyListener*    _self,
                           ATX_CString              name,
                           ATX_PropertyType         type,
                           const ATX_PropertyValue* value)
{
    Listener* self = ATX_SELF(Listener, ATX_PropertyListener);

    ATX_Debug("OnPropertyChanged[%s]: ", ATX_CSTR(self->name));
    if (value) {
        PrintProperty(name, type, value);
    } else {
        ATX_Debug("name=%s [REMOVED]\n", name);
    }
}
/*----------------------------------------------------------------------
|    AlsaOutput_QueryMediaType
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutput_QueryMediaType(BLT_MediaPort*        _self,
                          BLT_Ordinal           index,
                          const BLT_MediaType** media_type)
{
    AlsaOutput* self = ATX_SELF(AlsaOutput, BLT_MediaPort);

    if (index == 0) {
        *media_type = (const BLT_MediaType*)&self->expected_media_type;
        return BLT_SUCCESS;
    } else {
        *media_type = NULL;
        return BLT_FAILURE;
    }
}
Exemple #25
0
/*----------------------------------------------------------------------
|       StdcFile_GetOutputStream
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFile_GetOutputStream(ATX_File*          _self, 
                         ATX_OutputStream** stream)
{
    StdcFile* self = ATX_SELF(StdcFile, ATX_File);

    /* check that the file is open */
    if (self->file == NULL) return ATX_ERROR_FILE_NOT_OPEN;

    /* check that the mode is compatible */
    if (!(self->mode & ATX_FILE_OPEN_MODE_WRITE)) {
        return ATX_ERROR_FILE_NOT_WRITABLE;
    }

    return StdcFileOutputStream_Create(self->file, stream);
}
/*----------------------------------------------------------------------
|    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;
}
/*----------------------------------------------------------------------
|   DcfParserInput_QueryMediaType
+---------------------------------------------------------------------*/
BLT_METHOD
DcfParserInput_QueryMediaType(BLT_MediaPort*        _self,
                              BLT_Ordinal           index,
                              const BLT_MediaType** media_type)
{
    DcfParserInput* self = ATX_SELF(DcfParserInput, BLT_MediaPort);
    
    if (index == 0) {
        *media_type = &self->dcf1_media_type;
        return BLT_SUCCESS;
    } else if (index == 1) {
        *media_type = &self->dcf2_media_type;
        return BLT_SUCCESS;
    } else {
        *media_type = NULL;
        return BLT_FAILURE;
    }
}
/*----------------------------------------------------------------------
|   Mp4ParserInput_QueryMediaType
+---------------------------------------------------------------------*/
BLT_METHOD
Mp4ParserInput_QueryMediaType(BLT_MediaPort*        _self,
                              BLT_Ordinal           index,
                              const BLT_MediaType** media_type)
{
    Mp4ParserInput* self = ATX_SELF(Mp4ParserInput, BLT_MediaPort);
    
    if (index == 0) {
        *media_type = &self->audio_media_type;
        return BLT_SUCCESS;
    } else if (index == 0) {
        *media_type = &self->video_media_type;
        return BLT_SUCCESS;
    } else {
        *media_type = NULL;
        return BLT_FAILURE;
    }
}
Exemple #29
0
/*----------------------------------------------------------------------
|       StdcFileOutputStream_Write
+---------------------------------------------------------------------*/
ATX_METHOD
StdcFileOutputStream_Write(ATX_OutputStream* _self,
                           ATX_AnyConst      buffer, 
                           ATX_Size          bytes_to_write, 
                           ATX_Size*         bytes_written)
{
    StdcFileStream* self = ATX_SELF(StdcFileStream, ATX_OutputStream);
    size_t          nb_written;

    nb_written = fwrite(buffer, 1, (size_t)bytes_to_write, self->file->file);
    if (nb_written > 0 || bytes_to_write == 0) {
        if (bytes_written) *bytes_written = nb_written;
        self->file->position += nb_written;
        return ATX_SUCCESS;
    } else {
        if (bytes_written) *bytes_written = 0;
        return ATX_FAILURE;
    }

    return ATX_SUCCESS;
}
/*----------------------------------------------------------------------
|   FileOutput_GetStream
+---------------------------------------------------------------------*/
BLT_METHOD
FileOutput_GetStream(BLT_OutputStreamProvider* _self,
                     ATX_OutputStream**        stream,
                     const BLT_MediaType*      media_type)
{
    FileOutput* self = ATX_SELF(FileOutput, BLT_OutputStreamProvider);

    *stream = self->stream;
    ATX_REFERENCE_OBJECT(*stream);

    /* we're providing the stream, but we *receive* the type */
    if (media_type) {
        if (self->media_type->id == BLT_MEDIA_TYPE_ID_UNKNOWN) {
            BLT_MediaType_Free(self->media_type);
            BLT_MediaType_Clone(media_type, &self->media_type);
        } else if (self->media_type->id != media_type->id) {
            return BLT_ERROR_INVALID_MEDIA_TYPE;
        }
    }
    return BLT_SUCCESS;
}