/*---------------------------------------------------------------------- | CrossFader_Activate +---------------------------------------------------------------------*/ BLT_METHOD CrossFader_Activate(BLT_MediaNodeInstance* instance, BLT_Stream* stream) { ATX_Properties settings; CrossFader* fader = (CrossFader*)instance; /* stop listening for settings on the current stream, if any */ if (!ATX_OBJECT_IS_NULL(&fader->context) && fader->listener_handle != NULL) { if (BLT_SUCCEEDED(BLT_Core_GetSettings(&fader->base.core, &settings))) { ATX_Properties_RemoveListener(&settings, fader->listener_handle); } fader->listener_handle = NULL; } /* keep a reference to the stream */ fader->context = *stream; /* listen to settings on the new stream */ if (BLT_SUCCEEDED(BLT_Core_GetSettings(&fader->base.core, &settings))) { ATX_PropertyListener me; ATX_INSTANCE(&me) = (ATX_PropertyListenerInstance*)fader; ATX_INTERFACE(&me) = &CrossFader_ATX_PropertyListenerInterface; ATX_Properties_AddListener(&settings, "SomeSetting", &me, &fader->listener_handle); } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_Decoder_PumpPacketWithOptions +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_PumpPacketWithOptions(BLT_Decoder* decoder, BLT_Flags options) { BLT_Result result; /* check if we're in non-blocking mode */ if (options & BLT_DECODER_PUMP_OPTION_NON_BLOCKING) { if (decoder->output) { BLT_OutputNodeStatus status; result = BLT_OutputNode_GetStatus(decoder->output, &status); if (BLT_SUCCEEDED(result) && (status.flags & BLT_OUTPUT_NODE_STATUS_QUEUE_FULL)) { ATX_LOG_FINE("output queue full, would block"); /* ensure that the stream is not paused */ BLT_Stream_Start(decoder->stream); return BLT_ERROR_WOULD_BLOCK; } } } /* pump a packet */ result = BLT_Stream_PumpPacket(decoder->stream); if (BLT_FAILED(result)) return result; return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_Decoder_GetVolume +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_GetVolume(BLT_Decoder* decoder, float* volume) { BLT_MediaNode* output_node; BLT_Result result; /* check parameters */ if (volume == NULL) return BLT_ERROR_INVALID_PARAMETERS; /* default value */ *volume = 0.0f; /* get the volume from the output node */ result = BLT_Stream_GetOutputNode(decoder->stream, &output_node); if (BLT_SUCCEEDED(result) && output_node) { BLT_VolumeControl* volume_control = ATX_CAST(output_node, BLT_VolumeControl); if (volume_control) { result = BLT_VolumeControl_GetVolume(volume_control, volume); } else { result = BLT_ERROR_NOT_SUPPORTED; } ATX_RELEASE_OBJECT(output_node); } else { result = BLT_ERROR_INVALID_STATE; } return result; }
/*---------------------------------------------------------------------- | Mp4Parser_SetupAudioOutput +---------------------------------------------------------------------*/ static BLT_Result Mp4Parser_SetupAudioOutput(Mp4Parser* self, AP4_Movie* movie) { ATX_Properties* properties = NULL; // select the audio track if (BLT_SUCCEEDED(BLT_Stream_GetProperties(ATX_BASE(self, BLT_BaseMediaNode).context, &properties))) { ATX_PropertyValue value; bool selector_is_strict = false; if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_STREAM_AUDIO_TRACK_SELECTOR_STRICT_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_BOOLEAN) { selector_is_strict = value.data.boolean == ATX_TRUE; } } if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_STREAM_AUDIO_TRACK_SELECTOR_INDEX_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_INTEGER) { ATX_LOG_INFO_1("selecting audio track by index (%d)", value.data.integer); self->audio_output.track = movie->GetTrack(AP4_Track::TYPE_AUDIO, value.data.integer); if (self->audio_output.track == NULL) { ATX_LOG_INFO("track not found"); if (selector_is_strict) return BLT_SUCCESS; } } } else if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_STREAM_AUDIO_TRACK_SELECTOR_ID_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_INTEGER) { ATX_LOG_INFO_1("selecting audio track by ID (%d)", value.data.integer); self->audio_output.track = movie->GetTrack((AP4_UI32)value.data.integer); if (self->audio_output.track == NULL) { ATX_LOG_INFO("track not found"); if (selector_is_strict) return BLT_SUCCESS; } else if (self->audio_output.track->GetType() != AP4_Track::TYPE_AUDIO) { ATX_LOG_INFO("track is not audio"); if (selector_is_strict) return BLT_SUCCESS; } } } } // select the first audio track if no specific track was selected if (self->audio_output.track == NULL) { ATX_LOG_INFO("selecting first audio track"); self->audio_output.track = movie->GetTrack(AP4_Track::TYPE_AUDIO); } // exit now if the track does not exist if (self->audio_output.track == NULL) return BLT_SUCCESS; ATX_LOG_INFO_1("found audio track (id=%d)", self->audio_output.track->GetId()); // use the first sample description by default return Mp4ParserOutput_SetSampleDescription(&self->audio_output, 0); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::OnSetOutputCommand +---------------------------------------------------------------------*/ void BLT_DecoderServer::OnSetOutputCommand(BLT_CString name, BLT_CString type) { BLT_Result result; ATX_LOG_FINE_2("set output (%s / %s", BLT_SAFE_STRING(name), BLT_SAFE_STRING(type)); result = BLT_Decoder_SetOutput(m_Decoder, name, type); if (BLT_SUCCEEDED(result)) { // notify of the new volume float volume=0.0f; result = BLT_Decoder_GetVolume(m_Decoder, &volume); if (BLT_SUCCEEDED(result)) { m_Client->PostMessage(new BLT_DecoderClient_VolumeNotificationMessage(volume)); } result = BLT_SUCCESS; } SendReply(BLT_DecoderServer_Message::COMMAND_ID_SET_OUTPUT, result); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::WaitForTerminateMessage +---------------------------------------------------------------------*/ void BLT_DecoderServer::WaitForTerminateMessage() { BLT_Result result; do { result = m_MessageQueue->PumpMessage(NPT_TIMEOUT_INFINITE); } while (BLT_SUCCEEDED(result)); SetState(STATE_TERMINATED); }
/*---------------------------------------------------------------------- | BLT_Decoder_UpdateStatus +---------------------------------------------------------------------*/ static BLT_Result BLT_Decoder_UpdateStatus(BLT_Decoder* decoder) { BLT_StreamStatus status; BLT_StreamInfo stream_info; BLT_Result result; result = BLT_Stream_GetStatus(decoder->stream, &status); if (BLT_SUCCEEDED(result)) { decoder->status.time_stamp = status.time_stamp; decoder->status.position = status.position; } result = BLT_Stream_GetInfo(decoder->stream, &stream_info); if (BLT_SUCCEEDED(result)) { decoder->status.stream_info = stream_info; } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_DecoderServer::SendReply +---------------------------------------------------------------------*/ BLT_Result BLT_DecoderServer::SendReply(BLT_DecoderServer_Message::CommandId id, BLT_Result result) { BLT_DecoderClient_Message* reply; if (BLT_SUCCEEDED(result)) { reply = new BLT_DecoderClient_AckNotificationMessage(id); } else { reply = new BLT_DecoderClient_NackNotificationMessage(id, result); } return m_Client->PostMessage(reply); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::OnSeekToTimeCommand +---------------------------------------------------------------------*/ void BLT_DecoderServer::OnSeekToTimeCommand(BLT_UInt64 time) { BLT_Result result; ATX_LOG_FINE_1("[%d]", (int)time); result = BLT_Decoder_SeekToTime(m_Decoder, time); if (BLT_SUCCEEDED(result)) { UpdateStatus(); // update the state we were in the STATE_EOS state if (m_State == STATE_EOS) SetState(STATE_STOPPED); } SendReply(BLT_DecoderServer_Message::COMMAND_ID_SEEK_TO_TIME, result); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::OnSeekToPositionCommnand +---------------------------------------------------------------------*/ void BLT_DecoderServer::OnSeekToPositionCommand(BLT_UInt64 offset, BLT_UInt64 range) { BLT_Result result; ATX_LOG_FINE_2("[%d:%d]", (int)offset, (int)range); result = BLT_Decoder_SeekToPosition(m_Decoder, offset, range); if (BLT_SUCCEEDED(result)) { UpdateStatus(); // update the state we were in the STATE_EOS state if (m_State == STATE_EOS) SetState(STATE_STOPPED); } SendReply(BLT_DecoderServer_Message::COMMAND_ID_SEEK_TO_POSITION, result); }
/*---------------------------------------------------------------------- | CrossFader_Deactivate +---------------------------------------------------------------------*/ BLT_METHOD CrossFader_Deactivate(BLT_MediaNodeInstance* instance) { ATX_Properties settings; CrossFader* fader = (CrossFader*)instance; /* remove our listener */ if (BLT_SUCCEEDED(BLT_Core_GetSettings(&fader->base.core, &settings))) { ATX_Properties_RemoveListener(&settings, &fader->listener_handle); } /* we're detached from the stream */ ATX_CLEAR_OBJECT(&fader->context); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_Decoder_SetOutputNode +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_SetOutputNode(BLT_Decoder* decoder, BLT_CString name, BLT_MediaNode* node) { BLT_Result result; /* clear the status */ BLT_Decoder_ClearStatus(decoder); /* set the input of the stream */ result = BLT_Stream_SetOutputNode(decoder->stream, name, node); if (BLT_SUCCEEDED(result)) { BLT_Decoder_OutputChanged(decoder, node); } return result; }
/*---------------------------------------------------------------------- | DcfParser_Construct +---------------------------------------------------------------------*/ static void DcfParser_Construct(DcfParser* self, BLT_Module* module, BLT_Core* core) { /* construct the inherited object */ BLT_BaseMediaNode_Construct(&ATX_BASE(self, BLT_BaseMediaNode), module, core); /* construct the members */ DcfParserInput_Construct(&self->input, module); DcfParserOutput_Construct(&self->output); /* look for a key manager */ ATX_Properties* properties = NULL; if (BLT_SUCCEEDED(BLT_Core_GetProperties(core, &properties))) { ATX_PropertyValue value; if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_KEY_MANAGER_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_POINTER) { self->key_manager = (BLT_KeyManager*)value.data.pointer; } } else { ATX_LOG_FINE("no key manager"); } /* check if we need to use a cipher factory */ if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_CIPHER_FACTORY_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_POINTER) { self->cipher_factory = new BLT_Ap4CipherFactoryAdapter((BLT_CipherFactory*)value.data.pointer); } } else { ATX_LOG_FINE("no cipher factory"); } } /* setup interfaces */ ATX_SET_INTERFACE_EX(self, DcfParser, BLT_BaseMediaNode, BLT_MediaNode); ATX_SET_INTERFACE_EX(self, DcfParser, BLT_BaseMediaNode, ATX_Referenceable); ATX_SET_INTERFACE(&self->input, DcfParserInput, BLT_MediaPort); ATX_SET_INTERFACE(&self->input, DcfParserInput, BLT_InputStreamUser); ATX_SET_INTERFACE(&self->output, DcfParserOutput, BLT_MediaPort); ATX_SET_INTERFACE(&self->output, DcfParserOutput, BLT_InputStreamProvider); }
/*---------------------------------------------------------------------- | BLT_Decoder_Drain +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_Drain(BLT_Decoder* decoder) { BLT_MediaNode* output_node; BLT_Result result; /* drain the output node */ result = BLT_Stream_GetOutputNode(decoder->stream, &output_node); if (BLT_SUCCEEDED(result) && output_node) { BLT_OutputNode* output_node_interface = ATX_CAST(output_node, BLT_OutputNode); if (output_node_interface) { result = BLT_OutputNode_Drain(output_node_interface); } else { result = BLT_SUCCESS; } ATX_RELEASE_OBJECT(output_node); } else { result = BLT_ERROR_INVALID_STATE; } return result; }
/*---------------------------------------------------------------------- | BLT_Decoder_SetVolume +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_SetVolume(BLT_Decoder* decoder, float volume) { BLT_MediaNode* output_node; BLT_Result result; /* get the volume from the output node */ result = BLT_Stream_GetOutputNode(decoder->stream, &output_node); if (BLT_SUCCEEDED(result) && output_node) { BLT_VolumeControl* volume_control = ATX_CAST(output_node, BLT_VolumeControl); if (volume_control) { result = BLT_VolumeControl_SetVolume(volume_control, volume); } else { result = BLT_ERROR_NOT_SUPPORTED; } ATX_RELEASE_OBJECT(output_node); } else { result = BLT_ERROR_INVALID_STATE; } return result; }
/*---------------------------------------------------------------------- | BLT_Decoder_SetOutput +---------------------------------------------------------------------*/ BLT_Result BLT_Decoder_SetOutput(BLT_Decoder* decoder, BLT_CString name, BLT_CString type) { BLT_Result result; /* normalize the name and type */ if (name && name[0] == '\0') name = NULL; if (type && type[0] == '\0') type = NULL; if (name == NULL) { /* if the name is NULL or empty, it means reset */ BLT_Decoder_OutputChanged(decoder, NULL); return BLT_Stream_ResetOutput(decoder->stream); } else { if (ATX_StringsEqual(name, BLT_DECODER_DEFAULT_OUTPUT_NAME)) { /* if the name is BLT_DECODER_DEFAULT_OUTPUT_NAME, use default */ BLT_CString default_name; BLT_CString default_type; BLT_Builtins_GetDefaultAudioOutput(&default_name, &default_type); name = default_name; if (type == NULL) type = default_type; result = BLT_Stream_SetOutput(decoder->stream, name, type); } else { /* set the output of the stream by name */ result = BLT_Stream_SetOutput(decoder->stream, name, type); } } if (BLT_SUCCEEDED(result)) { BLT_MediaNode* node = NULL; BLT_Stream_GetOutputNode(decoder->stream, &node); BLT_Decoder_OutputChanged(decoder, node); ATX_RELEASE_OBJECT(node); } return result; }
/*---------------------------------------------------------------------- | Mp4ParserOutput_ProcessCryptoInfo +---------------------------------------------------------------------*/ static BLT_Result Mp4ParserOutput_ProcessCryptoInfo(Mp4ParserOutput* self, AP4_SampleDescription*& sample_desc) { // check if the track is encrypted if (sample_desc->GetType() == AP4_SampleDescription::TYPE_PROTECTED) { ATX_LOG_FINE("track is encrypted"); AP4_ProtectedSampleDescription* prot_desc = dynamic_cast<AP4_ProtectedSampleDescription*>(sample_desc); if (prot_desc == NULL) { ATX_LOG_FINE("unable to obtain cipher info"); return BLT_ERROR_INVALID_MEDIA_FORMAT; } // obtain the key manager if (self->parser->key_manager == NULL) { ATX_Properties* properties = NULL; if (BLT_SUCCEEDED(BLT_Core_GetProperties(ATX_BASE(self->parser, BLT_BaseMediaNode).core, &properties))) { ATX_PropertyValue value; if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_KEY_MANAGER_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_POINTER) { self->parser->key_manager = (BLT_KeyManager*)value.data.pointer; } } else { ATX_LOG_FINE("no key manager"); } } } if (self->parser->key_manager == NULL) return BLT_ERROR_NO_MEDIA_KEY; // check if we need to use a cipher factory if (self->parser->cipher_factory == NULL) { ATX_Properties* properties = NULL; if (BLT_SUCCEEDED(BLT_Core_GetProperties(ATX_BASE(self->parser, BLT_BaseMediaNode).core, &properties))) { ATX_PropertyValue value; if (ATX_SUCCEEDED(ATX_Properties_GetProperty(properties, BLT_CIPHER_FACTORY_PROPERTY, &value))) { if (value.type == ATX_PROPERTY_VALUE_TYPE_POINTER) { self->parser->cipher_factory = new BLT_Ap4CipherFactoryAdapter((BLT_CipherFactory*)value.data.pointer); } } } } // figure out the content ID for this track // TODO: support different content ID schemes // for now, we just make up a content ID based on the track ID char content_id[32]; NPT_FormatString(content_id, sizeof(content_id), "@track.%d", self->track->GetId()); // get the key for this content unsigned int key_size = 256; NPT_DataBuffer key(key_size); BLT_Result result = BLT_KeyManager_GetKeyByName(self->parser->key_manager, content_id, key.UseData(), &key_size); if (result == ATX_ERROR_NOT_ENOUGH_SPACE) { key.SetDataSize(key_size); result = BLT_KeyManager_GetKeyByName(self->parser->key_manager, content_id, key.UseData(), &key_size); } if (BLT_FAILED(result)) return BLT_ERROR_NO_MEDIA_KEY; key.SetDataSize(key_size); delete self->sample_decrypter; self->sample_decrypter = AP4_SampleDecrypter::Create(prot_desc, key.GetData(), key_size, self->parser->cipher_factory); if (self->sample_decrypter == NULL) { ATX_LOG_FINE("unable to create decrypter"); return BLT_ERROR_CRYPTO_FAILURE; } // switch to the original sample description sample_desc = prot_desc->GetOriginalSampleDescription(); } return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | BLT_DecoderServer::Run +---------------------------------------------------------------------*/ void BLT_DecoderServer::Run() { BLT_Result result; ATX_LOG_INFO("running"); // create the decoder result = BLT_Decoder_Create(&m_Decoder); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_Create")); WaitForTerminateMessage(); return; } // register as the event handler BLT_Decoder_SetEventListener(m_Decoder, &ATX_BASE(&m_EventListener, BLT_EventListener)); // listen to core property changes { ATX_Properties* properties; BLT_Decoder_GetProperties(m_Decoder, &properties); ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_CorePropertyListener, ATX_PropertyListener), NULL); } // listen to stream property changes { ATX_Properties* properties; BLT_Decoder_GetStreamProperties(m_Decoder, &properties); ATX_Properties_AddListener(properties, NULL, &ATX_BASE(&m_StreamPropertyListener, ATX_PropertyListener), NULL); } // register builtins result = BLT_Decoder_RegisterBuiltins(m_Decoder); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_RegisterBuiltins")); WaitForTerminateMessage(); return; } // set default output, default type result = BLT_Decoder_SetOutput(m_Decoder, BLT_DECODER_DEFAULT_OUTPUT_NAME, NULL); if (BLT_FAILED(result)) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_INIT_ERROR, result, "error from BLT_Decoder_SetOutput")); //WaitForTerminateMessage(); //return; } // notify the client of the initial state m_Client->PostMessage( new BLT_DecoderClient_DecoderStateNotificationMessage(STATE_STOPPED)); // initial status BLT_Decoder_GetStatus(m_Decoder, &m_DecoderStatus); m_DecoderStatus.position.range = m_PositionUpdateRange; NotifyTimeCode(); NotifyPosition(); // initial volume float volume=0.0f; result = BLT_Decoder_GetVolume(m_Decoder, &volume); if (BLT_SUCCEEDED(result)) { m_Client->PostMessage(new BLT_DecoderClient_VolumeNotificationMessage(volume)); } SetupIsComplete(); // decoding loop do { do { result = m_MessageQueue->PumpMessage(0); // non-blocking } while (BLT_SUCCEEDED(result)); if (result != NPT_ERROR_LIST_EMPTY) { break; } if (m_State == STATE_PLAYING) { result = BLT_Decoder_PumpPacketWithOptions(m_Decoder, BLT_DECODER_PUMP_OPTION_NON_BLOCKING); if (BLT_FAILED(result)) { if (result == BLT_ERROR_WOULD_BLOCK || result == BLT_ERROR_PORT_HAS_NO_DATA) { /* not fatal, just wait and try again later */ ATX_LOG_FINER("pump would block, waiting a short time"); result = m_MessageQueue->PumpMessage(BLT_PLAYER_LOOP_WAIT_DURATION); } else { ATX_LOG_FINE_1("stopped on %d", result); if (result != BLT_ERROR_EOS) { m_Client->PostMessage(new BLT_DecoderClient_DecoderEventNotificationMessage( BLT_DecoderServer::DecoderEvent::EVENT_TYPE_DECODING_ERROR, result, "error from BLT_Decoder_PumpPacketWithOptions")); } SetState(STATE_EOS); result = BLT_SUCCESS; } } else { UpdateStatus(); } } else { ATX_LOG_FINE("waiting for message"); result = m_MessageQueue->PumpMessage(NPT_TIMEOUT_INFINITE); ATX_LOG_FINE("got message"); } } while (BLT_SUCCEEDED(result) || result == NPT_ERROR_TIMEOUT); ATX_LOG_FINE("received Terminate Message"); // unregister as an event listener BLT_Decoder_SetEventListener(m_Decoder, NULL); // destroy the decoder if (m_Decoder != NULL) { BLT_Decoder_Destroy(m_Decoder); } // we're done SetState(STATE_TERMINATED); }