/*----------------------------------------------------------------------
|   DcfParserModule_Attach
+---------------------------------------------------------------------*/
BLT_METHOD
DcfParserModule_Attach(BLT_Module* _self, BLT_Core* core)
{
    DcfParserModule* self = ATX_SELF_EX(DcfParserModule, BLT_BaseModule, BLT_Module);
    BLT_Registry*    registry;
    BLT_Result       result;

    /* get the registry */
    result = BLT_Core_GetRegistry(core, &registry);
    if (BLT_FAILED(result)) return result;

    /* register the ".dcf" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".dcf",
                                            "application/vnd.oma.drm.content");
    if (BLT_FAILED(result)) return result;

    /* register the ".odf" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".odf",
                                            "application/vnd.oma.drm.dcf");
    if (BLT_FAILED(result)) return result;

    /* register the ".oda" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".oda",
                                            "application/vnd.oma.drm.dcf");
    if (BLT_FAILED(result)) return result;

    /* register the ".odv" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".odv",
                                            "application/vnd.oma.drm.dcf");
    if (BLT_FAILED(result)) return result;

    /* get the type id for "application/vnd.oma.drm.content" */
    result = BLT_Registry_GetIdForName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        "application/vnd.oma.drm.content",
        &self->dcf1_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1("DCF Parser Module::Attach (application/vnd.oma.drm.content type = %d)", self->dcf1_type_id);
    
    /* get the type id for "application/vnd.oma.drm.dcf" */
    result = BLT_Registry_GetIdForName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        "application/vnd.oma.drm.dcf",
        &self->dcf2_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1("DCF Parser Module::Attach (application/vnd.oma.drm.dcf type = %d)", self->dcf2_type_id);

    return BLT_SUCCESS;
}
Beispiel #2
0
/*----------------------------------------------------------------------
|   BLT_BaseModule_CreateEx
+---------------------------------------------------------------------*/
BLT_Result
BLT_BaseModule_CreateEx(BLT_CString                name, 
                        BLT_CString                uid,
                        BLT_Flags                  flags,
                        BLT_Cardinal               property_count,
                        const ATX_Property*        properties,
                        const BLT_ModuleInterface* module_interface,
                        const ATX_ReferenceableInterface* referenceable_interface,
                        BLT_Size                   instance_size,
                        BLT_Module**               object)
{
    BLT_BaseModule* module;

    ATX_LOG_FINE_1("creating module name=%s", name);
    
    /* allocate memory for the object */
    if (instance_size == 0) instance_size = sizeof(BLT_BaseModule);
    module = (BLT_BaseModule*)ATX_AllocateZeroMemory(instance_size);
    if (module == NULL) {
        *object = NULL;
        return ATX_ERROR_OUT_OF_MEMORY;
    }

    /* construct the object */
    BLT_BaseModule_ConstructEx(module, name, uid, flags, property_count, properties);

    /* setup interfaces */
    ATX_BASE(module, BLT_Module).iface = module_interface;
    ATX_BASE(module, ATX_Referenceable).iface = referenceable_interface;
    *object = &ATX_BASE(module, BLT_Module);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   BLT_DecoderServer::OnSetPropertyCommand
+---------------------------------------------------------------------*/
void 
BLT_DecoderServer::OnSetPropertyCommand(BLT_PropertyScope        scope,
                                        const NPT_String&        /*target*/,
                                        const NPT_String&        name,
                                        const ATX_PropertyValue* value)
{
    BLT_Result result;
    ATX_LOG_FINE_1("[%s]", name.GetChars());

    ATX_Properties* properties = NULL;
    switch (scope) {
        case BLT_PROPERTY_SCOPE_CORE:
            result = BLT_Decoder_GetProperties(m_Decoder, &properties);
            break;
            
        case BLT_PROPERTY_SCOPE_STREAM:
            result = BLT_Decoder_GetStreamProperties(m_Decoder, &properties);
            break;
            
        default:
            // not handled yet
            result = BLT_ERROR_NOT_SUPPORTED;
    }
    if (ATX_SUCCEEDED(result) && properties != NULL) {
        result = ATX_Properties_SetProperty(properties, name.GetChars(), value);
    }
    SendReply(BLT_DecoderServer_Message::COMMAND_ID_SET_PROPERTY, result);
}
/*----------------------------------------------------------------------
|    AlsaOutput_Open
+---------------------------------------------------------------------*/
static BLT_Result
AlsaOutput_Open(AlsaOutput* self)
{
    int io_result;

    ATX_LOG_FINE_1("openning output - name=%s", ATX_CSTR(self->device_name));

    switch (self->state) {
      case BLT_ALSA_OUTPUT_STATE_CLOSED:
        ATX_LOG_FINER("snd_pcm_open");
        io_result = snd_pcm_open(&self->device_handle,
                                 ATX_CSTR(self->device_name),
                                 SND_PCM_STREAM_PLAYBACK,
                                 0);
        if (io_result != 0) {
            self->device_handle = NULL;
            return BLT_FAILURE;
        }
        break;

      case BLT_ALSA_OUTPUT_STATE_OPEN:
        /* ignore */
        return BLT_SUCCESS;

      case BLT_ALSA_OUTPUT_STATE_CONFIGURED:
      case BLT_ALSA_OUTPUT_STATE_PREPARED:
        return BLT_FAILURE;
    }

    /* update the state */
    AlsaOutput_SetState(self, BLT_ALSA_OUTPUT_STATE_OPEN);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   DcfParserInput_SetStream
+---------------------------------------------------------------------*/
BLT_METHOD
DcfParserInput_SetStream(BLT_InputStreamUser* _self,
                         ATX_InputStream*     stream,
                         const BLT_MediaType* stream_media_type)
{
    DcfParser* self = ATX_SELF_M(input, DcfParser, BLT_InputStreamUser);
    BLT_Result result;
    
    /* check parameters and media type */
    if (stream == NULL            ||
        stream_media_type == NULL || 
       (stream_media_type->id != self->input.dcf1_media_type.id &&
        stream_media_type->id != self->input.dcf2_media_type.id)) {
        return BLT_ERROR_INVALID_MEDIA_FORMAT;
    }

    /* if we had a stream, release it */
    ATX_RELEASE_OBJECT(self->output.stream);

    /* reset field values */
    self->input.encrypted_size  = 0;
    self->input.content_type[0] = 0;
    self->input.content_uri[0]  = 0;
    self->output.size           = 0;
    
    /* parse the stream header/info */
    if (stream_media_type->id == self->input.dcf1_media_type.id) {
        result = DcfParser_ParseV1Header(self, stream);
    } else {
        result = DcfParser_ParseV2Header(self, stream);
    }
    if (BLT_FAILED(result)) return result;

    /* lookup the media type id */
    BLT_Registry* registry = NULL;
    result = BLT_Core_GetRegistry(ATX_BASE(self, BLT_BaseMediaNode).core, &registry);
    if (BLT_FAILED(result)) return result;
    result = BLT_Registry_GetIdForName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        self->input.content_type,
        &self->output.media_type.id);
    if (BLT_FAILED(result)) {
        ATX_LOG_FINE_1("unregistered content type (%s)", self->input.content_type);
        return BLT_ERROR_UNSUPPORTED_CODEC;
    }
        
    /* update the stream info */
    BLT_StreamInfo stream_info;
    stream_info.size      = self->output.size;
    stream_info.data_type = self->input.content_type;
    stream_info.mask      = BLT_STREAM_INFO_MASK_SIZE | BLT_STREAM_INFO_MASK_DATA_TYPE;
    
    /* update the stream info */
    if (stream_info.mask && ATX_BASE(self, BLT_BaseMediaNode).context) {
        BLT_Stream_SetInfo(ATX_BASE(self, BLT_BaseMediaNode).context, &stream_info);
    }

    return result;
}
/*----------------------------------------------------------------------
|   WaveFormatterModule_Attach
+---------------------------------------------------------------------*/
BLT_METHOD
WaveFormatterModule_Attach(BLT_Module* _self, BLT_Core* core)
{
    WaveFormatterModule* self = ATX_SELF_EX(WaveFormatterModule, BLT_BaseModule, BLT_Module);
    BLT_Registry*        registry;
    BLT_Result           result;

    /* get the registry */
    result = BLT_Core_GetRegistry(core, &registry);
    if (BLT_FAILED(result)) return result;

    /* register the .wav file extensions */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".wav",
                                            "audio/wav");
    if (BLT_FAILED(result)) return result;

    /* register the "audio/wav" type */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        "audio/wav",
        &self->wav_type_id);
    if (BLT_FAILED(result)) return result;
    
    ATX_LOG_FINE_1("WaveFormatterModule::Attach (audio/wav type = %d)", self->wav_type_id);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   BLT_DecoderServer::OnAddNodeCommnand
+---------------------------------------------------------------------*/
void
BLT_DecoderServer::OnAddNodeCommand(BLT_CString name)
{
    BLT_Result result;
    ATX_LOG_FINE_1("node name = %s", name);
    result = BLT_Decoder_AddNodeByName(m_Decoder, NULL, name);
    SendReply(BLT_DecoderServer_Message::COMMAND_ID_ADD_NODE, result);
}
Beispiel #8
0
/*----------------------------------------------------------------------
|   ATX_HttpClient_SendRequest
+---------------------------------------------------------------------*/
ATX_Result
ATX_HttpClient_SendRequest(ATX_HttpClient*    self, 
                           ATX_HttpRequest*   request, 
                           ATX_HttpResponse** response)
{
    ATX_Cardinal watchdog = ATX_HTTP_MAX_REDIRECTS;
    ATX_Boolean  keep_going;
    ATX_Result   result;

    do {
        keep_going = ATX_FALSE;
        result = ATX_HttpClient_SendRequestOnce(self, request, response);
        if (ATX_FAILED(result)) break;
        if (*response && self->options.follow_redirect &&
            (ATX_String_Equals(&request->method, ATX_HTTP_METHOD_GET, ATX_FALSE) ||
             ATX_String_Equals(&request->method, ATX_HTTP_METHOD_HEAD, ATX_FALSE)) &&
            ((*response)->status_code == 301 ||
             (*response)->status_code == 302 ||
             (*response)->status_code == 303 ||
             (*response)->status_code == 307)) {
            /* handle redirect */
            const ATX_String* location = 
                ATX_HttpMessage_GetHeader((ATX_HttpMessage*)*response,
                                          ATX_HTTP_HEADER_LOCATION);
            if (location) {
                /* replace the request url */
                ATX_HttpUrl url;
                result = ATX_HttpUrl_Construct(&url, ATX_String_GetChars(location));
                if (ATX_SUCCEEDED(result)) {
                    ATX_LOG_FINE_1("ATX_HttpClient::SendRequest - redirecting to %s",
                                   ATX_String_GetChars(location));
                    ATX_HttpUrl_Destruct(&request->url);
                    request->url = url;
                    keep_going = ATX_TRUE;
                    ATX_HttpResponse_Destroy(*response);
                    *response = NULL;
                } else {
                    ATX_LOG_FINE_1("ATX_HttpClient::SendRequest - failed to create redirection URL (%d)", result);
                    break;
                }
            }
        }       
    } while (keep_going && watchdog--);

    return result;
}
/*----------------------------------------------------------------------
|       AlsaOutputModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
AlsaOutputModule_Probe(BLT_Module*              self, 
                       BLT_Core*                core,
                       BLT_ModuleParametersType parameters_type,
                       BLT_AnyConst             parameters,
                       BLT_Cardinal*            match)
{
    BLT_COMPILER_UNUSED(self);
    BLT_COMPILER_UNUSED(core);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* the input protocol should be PACKET and the */
            /* output protocol should be NONE              */
            if ((constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_PACKET) ||
                (constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_NONE)) {
                return BLT_FAILURE;
            }

            /* the input type should be unknown, or audio/pcm */
            if (!(constructor->spec.input.media_type->id == 
                  BLT_MEDIA_TYPE_ID_AUDIO_PCM) &&
                !(constructor->spec.input.media_type->id == 
                  BLT_MEDIA_TYPE_ID_UNKNOWN)) {
                return BLT_FAILURE;
            }

            /* the name should be 'alsa:<name>' */
            if (constructor->name == NULL ||
                !ATX_StringsEqualN(constructor->name, "alsa:", 4)) {
                return BLT_FAILURE;
            }

            /* always an exact match, since we only respond to our name */
            *match = BLT_MODULE_PROBE_MATCH_EXACT;

            ATX_LOG_FINE_1("probe ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
/*----------------------------------------------------------------------
|   BLT_DecoderServer::OnSetVolumeCommand
+---------------------------------------------------------------------*/
void 
BLT_DecoderServer::OnSetVolumeCommand(float volume)
{
    BLT_Result result;
    ATX_LOG_FINE_1("volume=%f", volume);

    result = BLT_Decoder_SetVolume(m_Decoder, volume);
    SendReply(BLT_DecoderServer_Message::COMMAND_ID_SET_VOLUME, result);
}
    virtual void OnPongNotification(const void* cookie) {
        ATX_LOG_FINE_1("PONG: %x", (int)cookie);
        if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return;

        jintArray array = (jintArray)m_JniEnv->NewIntArray(1);
        jint value = (jint)cookie;
        m_JniEnv->SetIntArrayRegion(array, 0, 1, &value);

        m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_PONG, NULL, array);
    }
    virtual void OnVolumeNotification(float volume) {
        ATX_LOG_FINE_1("VOLUME: %f", volume);
        if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return;

        jintArray array = (jintArray)m_JniEnv->NewIntArray(4);
        jint values[2] = {(jint)(volume*100.0f), 100};
        m_JniEnv->SetIntArrayRegion(array, 0, 2, values);

        m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_VOLUME, NULL, array);
    }
    // event listener methods
    virtual void OnAckNotification(BLT_DecoderServer_Message::CommandId id) {
        ATX_LOG_FINE_1("ACK: %d", id);
        if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return;

        jintArray array = (jintArray)m_JniEnv->NewIntArray(1);
        jint value = MapCommandId(id);
        m_JniEnv->SetIntArrayRegion(array, 0, 1, &value);
        
        m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_ACK, NULL, array);
    }
    virtual void OnDecoderStateNotification(BLT_DecoderServer::State state) {
        ATX_LOG_FINE_1("DECODER-STATE: %d", (int)state);
        if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return;

        jintArray array = (jintArray)m_JniEnv->NewIntArray(1);
        jint value = MapDecoderState(state);
        m_JniEnv->SetIntArrayRegion(array, 0, 1, &value);
        
        m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_DECODER_STATE, NULL, array);
    }
Beispiel #15
0
/*----------------------------------------------------------------------
|   BLT_BaseModule_Destroy
+---------------------------------------------------------------------*/
BLT_Result
BLT_BaseModule_Destroy(BLT_BaseModule* module)
{
    ATX_LOG_FINE_1("destroying module name=%s", module->info.name);

    BLT_BaseModule_Destruct(module);
    ATX_FreeMemory((void*)module);
    
    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   FileOutputModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
FileOutputModule_Probe(BLT_Module*              self, 
                       BLT_Core*                core,
                       BLT_ModuleParametersType parameters_type,
                       BLT_AnyConst             parameters,
                       BLT_Cardinal*            match)
{
    BLT_COMPILER_UNUSED(self);
    BLT_COMPILER_UNUSED(core);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* the input protocol should be STREAM_PUSH and the */
            /* output protocol should be NONE                   */
            if ((constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.input.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_STREAM_PUSH) ||
                (constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.output.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_NONE)) {
                return BLT_FAILURE;
            }

            /* we need a name */
            if (constructor->name == NULL) {
                return BLT_FAILURE;
            }

            /* the name needs to be file:<filename> */
            if (!ATX_StringsEqualN(constructor->name, "file:", 5)) {
                return BLT_FAILURE;
            }

            /* always an exact match, since we only respond to our name */
            *match = BLT_MODULE_PROBE_MATCH_EXACT;

            ATX_LOG_FINE_1("FileOutputModule::Probe - Ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
Beispiel #17
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;
}
/*----------------------------------------------------------------------
|    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);
}
/*----------------------------------------------------------------------
|    OsxAudioUnitsOutput_CheckDownmix
+---------------------------------------------------------------------*/
static void
OsxAudioUnitsOutput_CheckDownmix(OsxAudioUnitsOutput* self)
{
    OSStatus err;
    UInt32   prop_size;
    
    err = AudioDeviceGetPropertyInfo(self->audio_device_id, 0, FALSE, kAudioDevicePropertyStreamConfiguration, &prop_size, NULL);
    if (err != noErr) return;

    AudioBufferList* buffers = (AudioBufferList *)ATX_AllocateZeroMemory(prop_size);
    err = AudioDeviceGetProperty(self->audio_device_id, 0, FALSE, kAudioDevicePropertyStreamConfiguration, &prop_size, buffers);
    if (err == noErr) {
        unsigned int i;
        unsigned int channel_count = 0;
        for (i = 0; i < buffers->mNumberBuffers; ++i) {
            channel_count += buffers->mBuffers[i].mNumberChannels;
        }
        ATX_LOG_FINE_1("device has %d channels", channel_count);
    }
    
    ATX_FreeMemory(buffers);
}
/*----------------------------------------------------------------------
|   AacDecoderModule_Attach
+---------------------------------------------------------------------*/
BLT_METHOD
AacDecoderModule_Attach(BLT_Module* _self, BLT_Core* core)
{
    AacDecoderModule* self = ATX_SELF_EX(AacDecoderModule, BLT_BaseModule, BLT_Module);
    BLT_Registry*           registry;
    BLT_Result              result;

    /* get the registry */
    result = BLT_Core_GetRegistry(core, &registry);
    if (BLT_FAILED(result)) return result;

    /* register the type id */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        BLT_MP4_AUDIO_ES_MIME_TYPE,
        &self->mp4es_type_id);
    if (BLT_FAILED(result)) return result;
    
    ATX_LOG_FINE_1("AacDecoderModule::Attach (" BLT_MP4_AUDIO_ES_MIME_TYPE " = %d)", self->mp4es_type_id);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   Mp4ParserModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
Mp4ParserModule_Probe(BLT_Module*              _self, 
                      BLT_Core*                core,
                      BLT_ModuleParametersType parameters_type,
                      BLT_AnyConst             parameters,
                      BLT_Cardinal*            match)
{
    Mp4ParserModule* self = ATX_SELF_EX(Mp4ParserModule, BLT_BaseModule, BLT_Module);
    BLT_COMPILER_UNUSED(core);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = (BLT_MediaNodeConstructor*)parameters;

            /* check if we're being probed by name */
            if (constructor->name != NULL) {
                /* we're being probed by name */
                if (ATX_StringsEqual(constructor->name, "com.bluetune.parsers.mp4")) {
                    /* our name */
                    *match = BLT_MODULE_PROBE_MATCH_EXACT;
                    return BLT_SUCCESS;
                } else {
                    /* not out name */
                    *match = 0;
                    return BLT_FAILURE;
                }
            }
            
            /* we need the input protocol to be STREAM_PULL and the output */
            /* protocol to be PACKET                                       */
             if ((constructor->spec.input.protocol  != BLT_MEDIA_PORT_PROTOCOL_ANY &&
                  constructor->spec.input.protocol  != BLT_MEDIA_PORT_PROTOCOL_STREAM_PULL) ||
                 (constructor->spec.output.protocol != BLT_MEDIA_PORT_PROTOCOL_ANY &&
                  constructor->spec.output.protocol != BLT_MEDIA_PORT_PROTOCOL_PACKET)) {
                return BLT_FAILURE;
            }

            /* we need the input media type to be 'audio/mp4' or 'video/mp4' */
            if (constructor->spec.input.media_type->id != self->mp4_audio_type_id &&
                constructor->spec.input.media_type->id != self->mp4_video_type_id) {
                return BLT_FAILURE;
            }

            /* the output type should be unknown at this point */
            if (constructor->spec.output.media_type->id != BLT_MEDIA_TYPE_ID_UNKNOWN) {
                return BLT_FAILURE;
            }

            /* set the match level */
            *match = BLT_MODULE_PROBE_MATCH_MAX - 10;

            ATX_LOG_FINE_1("match %d", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
/*----------------------------------------------------------------------
|   DcfParserModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
DcfParserModule_Probe(BLT_Module*              _self, 
                      BLT_Core*                core,
                      BLT_ModuleParametersType parameters_type,
                      BLT_AnyConst             parameters,
                      BLT_Cardinal*            match)
{
    DcfParserModule* self = ATX_SELF_EX(DcfParserModule, BLT_BaseModule, BLT_Module);
    BLT_COMPILER_UNUSED(core);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* we need the input protocol to be STREAM_PULL and the output */
            /* protocol to be STREAM_PULL                                  */
             if ((constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.input.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_STREAM_PULL) ||
                (constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.output.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_STREAM_PULL)) {
                return BLT_FAILURE;
            }

            /* we need the input media type to be 'application/vnd.oma.drm.content' */
            /* or 'application/vnd.oma.drm.dcf'                                     */
            if (constructor->spec.input.media_type->id != self->dcf1_type_id &&
                constructor->spec.input.media_type->id != self->dcf2_type_id) {
                return BLT_FAILURE;
            }

            /* the output type should be unknown at this point */
            if (constructor->spec.output.media_type->id != 
                BLT_MEDIA_TYPE_ID_UNKNOWN) {
                return BLT_FAILURE;
            }

            /* compute the match level */
            if (constructor->name != NULL) {
                /* we're being probed by name */
                if (ATX_StringsEqual(constructor->name, "DcfParser")) {
                    /* our name */
                    *match = BLT_MODULE_PROBE_MATCH_EXACT;
                } else {
                    /* not out name */
                    return BLT_FAILURE;
                }
            } else {
                /* we're probed by protocol/type specs only */
                *match = BLT_MODULE_PROBE_MATCH_MAX - 10;
            }

            ATX_LOG_FINE_1("DcfParserModule::Probe - Ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
/*----------------------------------------------------------------------
|    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);
}
/*----------------------------------------------------------------------
|   DcfParser_ParseV1Header
+---------------------------------------------------------------------*/
static BLT_Result
DcfParser_ParseV1Header(DcfParser* self, ATX_InputStream* stream)
{
    /* rewind the byte stream */
    ATX_InputStream_Seek(stream, 0);

    /* user a buffer for parsing */
    ATX_UInt8 buffer[3];
    
    /* read the first 3 fields */
    ATX_Result result;
    result = ATX_InputStream_ReadFully(stream, buffer, 3);
    if (ATX_FAILED(result)) return result;

    /* check the version */
    ATX_UInt8 version = buffer[0];
    if (version != 1) {
        ATX_LOG_FINE_1("unsupported DCF version (%d)", version);
        return BLT_ERROR_UNSUPPORTED_FORMAT;
    }
    
    /* read the content type */
    ATX_UInt8 content_type_length = buffer[1];
    self->input.content_type[content_type_length] = 0; // null-terminate
    result = ATX_InputStream_ReadFully(stream, self->input.content_type, content_type_length);
    if (ATX_FAILED(result)) return result;
    
    /* read the content URI */
    ATX_UInt8 content_uri_length = buffer[2];
    self->input.content_uri[content_uri_length] = 0; // null-terminate
    result = ATX_InputStream_ReadFully(stream, self->input.content_uri, content_uri_length);
    if (ATX_FAILED(result)) return result;
    
    /* read the variable-length fields */
    unsigned int var_length_1, var_length_2;
    ATX_UInt32 headers_length = 0;
    result = DcfParser_ReadUintvar(stream, headers_length, var_length_1);
    if (ATX_FAILED(result)) return result;
    ATX_UInt32 data_length = 0;
    result = DcfParser_ReadUintvar(stream, data_length, var_length_2);
    if (ATX_FAILED(result)) return result;

    /* check that the encrypted size makes sense */
    if (data_length < 32) return BLT_ERROR_INVALID_MEDIA_FORMAT;
    self->input.encrypted_size = data_length;
    
    /* get the content key */
    NPT_DataBuffer key;
    result = DcfParser_GetContentKey(self, self->input.content_uri, key);
    if (BLT_FAILED(result)) {
        ATX_LOG_FINE_2("GetKeyForContent(%s) returned %d", 
            self->input.content_uri, 
            result);
        return BLT_ERROR_NO_MEDIA_KEY;
    }

    /* read the headers */
    if (headers_length > BLT_DCF_PARSER_MAX_HEADERS_LENGTH) return BLT_ERROR_INVALID_MEDIA_FORMAT;
    char* headers = new char[headers_length+1];
    headers[headers_length] = '\0';
    result = ATX_InputStream_ReadFully(stream, headers, headers_length);
    if (ATX_FAILED(result)) {
        delete[] headers;
        return result;
    }
    
    /* as a first-order estimate, set the output size to the encrypted size minus the IV */
    self->output.size = self->input.encrypted_size-16;
        
    /* parse the headers */
    NPT_MemoryStream* headers_memory_stream = new NPT_MemoryStream(headers, headers_length);
    NPT_InputStreamReference headers_memory_stream_ref(headers_memory_stream);
    NPT_BufferedInputStream* headers_buffered_stream = 
        new NPT_BufferedInputStream(headers_memory_stream_ref);
    NPT_HttpHeaders content_headers;
    content_headers.Parse(*headers_buffered_stream);
    delete headers_buffered_stream;
    delete[] headers;
    
    /* find out about the encryption from the headers */
    const NPT_String* encryption_method = content_headers.GetHeaderValue("Encryption-Method");
    if (encryption_method == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT;
    
    /* check the encryption method */
    bool                           encryption_supported = false;
    NPT_Map<NPT_String,NPT_String> encryption_params;
    NPT_Size                       algorithm_id_length = 0;
    
    /* parse the algorithm id */
    int separator = encryption_method->Find(';', 0, true);
    if (separator > 0) {
        algorithm_id_length = separator;
        
        /* parse the params */
        result = NPT_ParseMimeParameters(((const char*)(*encryption_method))+separator+1, 
                                         encryption_params);
        if (NPT_FAILED(result)) {
            ATX_LOG_FINE_1("cannot parse Encryption-Method parameters (%s)", 
                           (const char*)encryption_method);
            return BLT_ERROR_INVALID_MEDIA_FORMAT;
        }

        /* parse the plaintext-length header, if present */
        NPT_String* plaintext_length = NULL;
        if (NPT_SUCCEEDED(encryption_params.Get("plaintext-length", plaintext_length))) {
            NPT_UInt64 value = 0;
            if (NPT_SUCCEEDED(plaintext_length->ToInteger64(value, true))) {
                self->output.size = value;
            }
        }
    } else {
        algorithm_id_length = encryption_method->GetLength();
    }       

    if (NPT_StringsEqualN((const char*)(*encryption_method),
                          BLT_DCF_PARSER_ALGORITHM_ID_AES128CBC,
                          algorithm_id_length)) {
        encryption_supported = true;
        NPT_String* padding = NULL;
        if (NPT_SUCCEEDED(encryption_params.Get("padding", padding))) {
            if (*padding == BLT_DCF_PARSER_PADDING_RFC2630) {
                encryption_supported = false; // hmmm, unknown padding
            }
        } 
    }

    if (!encryption_supported) {
        ATX_LOG_FINE_1("unsupported encryption format (%s)", encryption_method);
        return BLT_ERROR_UNSUPPORTED_FORMAT;
    }
    
    /* read the IV */
    AP4_UI08 iv[16];
    result = ATX_InputStream_ReadFully(stream, iv, sizeof(iv));
    if (BLT_FAILED(result)) return result;
    
    /* create a byte stream to represent the encrypted data */
    ATX_Size header_size = 3+content_type_length+content_uri_length+var_length_1+var_length_2+headers_length;
    ATX_InputStream* data_stream = NULL;
    ATX_SubInputStream_Create(stream, 
                              header_size+16, // skip the IV
                              self->input.encrypted_size-16, 
                              NULL, 
                              &data_stream);
    ATX_InputStream_To_AP4_ByteStream_Adapter* encrypted_stream = 
        new ATX_InputStream_To_AP4_ByteStream_Adapter(data_stream);
    ATX_RELEASE_OBJECT(data_stream);
    
    /* create a decrypting stream for the content */ // FIXME: temporary
    AP4_ByteStream* decrypting_stream = NULL;
    result = AP4_DecryptingStream::Create(AP4_BlockCipher::CBC,
                                          *encrypted_stream,
                                          self->output.size,
                                          iv, 16, 
                                          key.GetData(),
                                          key.GetDataSize(),
                                          self->cipher_factory,
                                          decrypting_stream);
    encrypted_stream->Release();
    if (AP4_FAILED(result)) return result;

    /* create a reverse adapter */
    result = AP4_ByteStream_To_ATX_InputStream_Adapter_Create(decrypting_stream, &self->output.stream);
    decrypting_stream->Release();

    return BLT_SUCCESS;
}
Beispiel #25
0
/*----------------------------------------------------------------------
|    SdlVideoOutput_PutPacket
+---------------------------------------------------------------------*/
BLT_METHOD
SdlVideoOutput_PutPacket(BLT_PacketConsumer* _self,
                         BLT_MediaPacket*    packet)
{
    SdlVideoOutput*              self = ATX_SELF(SdlVideoOutput, BLT_PacketConsumer);
    unsigned char*               pixel_data = (unsigned char*)BLT_MediaPacket_GetPayloadBuffer(packet);
    const BLT_RawVideoMediaType* media_type;
    unsigned int                 plane;
	SDL_Rect                     rect;
    
    /* check the media type */
    BLT_MediaPacket_GetMediaType(packet, (const BLT_MediaType**)&media_type);
    if (media_type->base.id != BLT_MEDIA_TYPE_ID_VIDEO_RAW) {
        ATX_LOG_FINE_1("rejecting media type id %d", media_type->base.id);
        return BLT_ERROR_INVALID_MEDIA_TYPE;
    }
    if (media_type->format != BLT_PIXEL_FORMAT_YV12) {
        ATX_LOG_FINE_1("rejecting pixel format %d", media_type->format);
        return BLT_ERROR_INVALID_MEDIA_TYPE;
    }

    /* resize/create the window and overlay if needed */
    if (self->yuv_overlay == NULL                 || 
        self->yuv_overlay->w != media_type->width ||
        self->yuv_overlay->h != media_type->height) {
        self->screen = SDL_SetVideoMode(media_type->width, media_type->height, 24, SDL_HWSURFACE | SDL_RESIZABLE);
        if (self->screen == NULL) {
            ATX_LOG_WARNING("SDL_SetVideoMode() failed");
            return BLT_FAILURE;
        }
        self->yuv_overlay = SDL_CreateYUVOverlay(media_type->width, media_type->height, SDL_YV12_OVERLAY, self->screen);
        if (self->yuv_overlay == NULL) {
            ATX_LOG_WARNING("SDL_CreateYUVOverlay() failed");
            return BLT_FAILURE;
        }
        
    }
    
    /* transfer the pixels */
    SDL_LockYUVOverlay(self->yuv_overlay);
    for (plane=0; plane<3; plane++) {
        unsigned int   plane_width  = (plane==0?media_type->width:(media_type->width/2));
        unsigned int   plane_height = (plane==0?media_type->height:(media_type->height/2));
        unsigned char* src          = pixel_data+media_type->planes[plane].offset;
        unsigned int   src_pitch    = media_type->planes[plane].bytes_per_line;
        unsigned char* dst          = self->yuv_overlay->pixels[plane==0?0:3-plane];
        unsigned int   dst_pitch    = self->yuv_overlay->pitches[plane==0?0:3-plane];
        while (plane_height--) {
            ATX_CopyMemory(dst, src,  plane_width);
            src += src_pitch;
            dst += dst_pitch;
        }
    }
    SDL_UnlockYUVOverlay(self->yuv_overlay);

    rect.x = 0;
	rect.y = 0;
	rect.w = self->screen->w;
	rect.h = self->screen->h;
	SDL_DisplayYUVOverlay(self->yuv_overlay, &rect);    
    
    {
        ATX_TimeStamp now;
        ATX_System_GetCurrentTimeStamp(&now);
        if (ATX_TimeStamp_IsLaterOrEqual(self->next_display_time, now)) { 
            ATX_TimeInterval delta;
            ATX_TimeStamp_Sub(delta, self->next_display_time, now);
            /* sanity check */
            if (delta.seconds == 0 && delta.nanoseconds > 1000000) {
                ATX_System_Sleep(&delta);
            }
        } else {
            self->next_display_time = now;
        }
        {
            ATX_TimeStamp frame_duration = {0, 41708000};
            ATX_TimeStamp_Add(self->next_display_time, self->next_display_time, frame_duration);
        }
    }
    
    return BLT_SUCCESS;
}
Beispiel #26
0
/*----------------------------------------------------------------------
|   CrossFaderModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
CrossFaderModule_Probe(BLT_ModuleInstance*      instance, 
                       BLT_Core*                core,
                       BLT_ModuleParametersType parameters_type,
                       BLT_AnyConst             parameters,
                       BLT_Cardinal*            match)
{
    BLT_COMPILER_UNUSED(core);
    BLT_COMPILER_UNUSED(instance);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* we need a name */
            if (constructor->name == NULL ||
                !ATX_StringsEqual(constructor->name, "CrossFader")) {
                return BLT_FAILURE;
            }

            /* the input and output protocols should be PACKET */
            if ((constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.input.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_PACKET) ||
                (constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.output.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_PACKET)) {
                return BLT_FAILURE;
            }

            /* the input type should be unspecified, or audio/pcm */
            if (!(constructor->spec.input.media_type->id == 
                  BLT_MEDIA_TYPE_ID_AUDIO_PCM) &&
                !(constructor->spec.input.media_type->id ==
                  BLT_MEDIA_TYPE_ID_UNKNOWN)) {
                return BLT_FAILURE;
            }

            /* the output type should be unspecified, or audio/pcm */
            if (!(constructor->spec.output.media_type->id == 
                  BLT_MEDIA_TYPE_ID_AUDIO_PCM) &&
                !(constructor->spec.output.media_type->id ==
                  BLT_MEDIA_TYPE_ID_UNKNOWN)) {
                return BLT_FAILURE;
            }

            /* match level is always exact */
            *match = BLT_MODULE_PROBE_MATCH_EXACT;

            ATX_LOG_FINE_1("CrossFaderModule::Probe - Ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
Beispiel #27
0
/*----------------------------------------------------------------------
|   PcmAdapterModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
PcmAdapterModule_Probe(BLT_Module*              self,  
                       BLT_Core*                core,
                       BLT_ModuleParametersType parameters_type,
                       BLT_AnyConst             parameters,
                       BLT_Cardinal*            match)
{
    BLT_COMPILER_UNUSED(self);
    BLT_COMPILER_UNUSED(core);

    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* compute match based on specified name */
            if (constructor->name == NULL) {
                *match = BLT_MODULE_PROBE_MATCH_DEFAULT;

                /* the input protocol should be PACKET */
                if (constructor->spec.input.protocol != 
                    BLT_MEDIA_PORT_PROTOCOL_PACKET) {
                    return BLT_FAILURE;
                }

                /* output protocol should be PACKET */
                if (constructor->spec.output.protocol != 
                    BLT_MEDIA_PORT_PROTOCOL_PACKET) {
                    return BLT_FAILURE;
                }

                /* check that the in and out formats are supported */
                if (!BLT_Pcm_CanConvert(constructor->spec.input.media_type, 
                                        constructor->spec.output.media_type)) {
                    return BLT_FAILURE;
                }
            } else {
                /* if a name is specified, it needs to match exactly */
                if (!ATX_StringsEqual(constructor->name, "PcmAdapter")) {
                    return BLT_FAILURE;
                } else {
                    *match = BLT_MODULE_PROBE_MATCH_EXACT;
                }

                /* the input protocol should be PACKET or ANY */
                if (constructor->spec.input.protocol !=
                    BLT_MEDIA_PORT_PROTOCOL_ANY &&
                    constructor->spec.input.protocol != 
                    BLT_MEDIA_PORT_PROTOCOL_PACKET) {
                    return BLT_FAILURE;
                }

                /* output protocol should be PACKET or ANY */
                if ((constructor->spec.output.protocol !=
                    BLT_MEDIA_PORT_PROTOCOL_ANY &&
                    constructor->spec.output.protocol != 
                    BLT_MEDIA_PORT_PROTOCOL_PACKET)) {
                    return BLT_FAILURE;
                }

                /* check that the in and out formats are supported */
                if (!BLT_Pcm_CanConvert(constructor->spec.input.media_type, 
                                        constructor->spec.output.media_type)) {
                    return BLT_FAILURE;
                }
            }

            ATX_LOG_FINE_1("PcmAdapterModule::Probe - Ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}
/*----------------------------------------------------------------------
|   Mp4Parser_Seek
+---------------------------------------------------------------------*/
BLT_METHOD
Mp4Parser_Seek(BLT_MediaNode* _self,
               BLT_SeekMode*  mode,
               BLT_SeekPoint* point)
{
    Mp4Parser* self = ATX_SELF_EX(Mp4Parser, BLT_BaseMediaNode, BLT_MediaNode);

    /* estimate the seek point */
    if (ATX_BASE(self, BLT_BaseMediaNode).context == NULL) return BLT_FAILURE;
    BLT_Stream_EstimateSeekPoint(ATX_BASE(self, BLT_BaseMediaNode).context, *mode, point);
    if (!(point->mask & BLT_SEEK_POINT_MASK_TIME_STAMP)) {
        return BLT_FAILURE;
    }

    /* seek to the estimated offset on all tracks */
    AP4_Ordinal sample_index = 0;
    AP4_UI32    ts_ms = point->time_stamp.seconds*1000+point->time_stamp.nanoseconds/1000000;
    if (self->video_output.track) {
        AP4_Result result = self->video_output.track->GetSampleIndexForTimeStampMs(ts_ms, sample_index);
        if (AP4_FAILED(result)) {
            ATX_LOG_WARNING_1("video GetSampleIndexForTimeStampMs failed (%d)", result);
            return BLT_FAILURE;
        }
        ATX_LOG_FINE_1("seeking to video time %d ms", ts_ms);
        
        // go to the nearest sync sample
        self->video_output.sample = self->video_output.track->GetNearestSyncSampleIndex(sample_index);
        if (self->input.reader) {
            self->input.reader->SetSampleIndex(self->video_output.track->GetId(), self->video_output.sample);
        }
        ATX_LOG_FINE_1("seeking to video sync sample %d", self->video_output.sample);
        
        // compute the timestamp of the video sample we're seeking to, so we can pick an audio
        // sample that is close in time (there are many more audio sync points than video)
        AP4_Sample sample;
        if (AP4_SUCCEEDED(self->video_output.track->GetSample(self->video_output.sample, sample))) {
            AP4_UI32 media_timescale = self->video_output.track->GetMediaTimeScale();
            if (media_timescale) {
                ts_ms = (AP4_UI32)((((AP4_UI64)sample.GetCts())*1000)/media_timescale);
                ATX_LOG_FINE_1("sync sample time is %d ms", ts_ms);
            }
        } else {
            ATX_LOG_FINE_1("unable to get sample info for sample %d", self->video_output.sample);
        }
    }
    if (self->audio_output.track) {
        AP4_Result result = self->audio_output.track->GetSampleIndexForTimeStampMs(ts_ms, sample_index);
        if (AP4_FAILED(result)) {
            ATX_LOG_WARNING_1("audio GetSampleIndexForTimeStampMs failed (%d)", result);
            return BLT_FAILURE;
        }
        self->audio_output.sample = sample_index;
        if (self->input.reader) {
            self->input.reader->SetSampleIndex(self->audio_output.track->GetId(), sample_index);
        }
    }
    
    /* set the mode so that the nodes down the chain know the seek has */
    /* already been done on the stream                                 */
    *mode = BLT_SEEK_MODE_IGNORE;

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   Mp4ParserModule_Attach
+---------------------------------------------------------------------*/
BLT_METHOD
Mp4ParserModule_Attach(BLT_Module* _self, BLT_Core* core)
{
    Mp4ParserModule* self = ATX_SELF_EX(Mp4ParserModule, BLT_BaseModule, BLT_Module);
    BLT_Registry*    registry;
    BLT_Result       result;

    /* get the registry */
    result = BLT_Core_GetRegistry(core, &registry);
    if (BLT_FAILED(result)) return result;

    /* register the ".mp4" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".mp4",
                                            "video/mp4");
    if (BLT_FAILED(result)) return result;

    /* register the ".m4a" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".m4a",
                                            "audio/mp4");
    if (BLT_FAILED(result)) return result;

    /* register the ".m4v" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".m4v",
                                            "video/mp4");
    if (BLT_FAILED(result)) return result;

    /* register the ".m4p" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".m4p",
                                            "video/mp4");
    if (BLT_FAILED(result)) return result;

    /* register the ".3gp" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".3gp",
                                            "video/mp4");
    if (BLT_FAILED(result)) return result;

    /* register the ".3gp" file extension */
    result = BLT_Registry_RegisterExtension(registry, 
                                            ".mov",
                                            "video/mp4");
    if (BLT_FAILED(result)) return result;

    /* get the type id for "audio/mp4" */
    result = BLT_Registry_GetIdForName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        "audio/mp4",
        &self->mp4_audio_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1("audio/mp4 type = %d", self->mp4_audio_type_id);
    
    /* get the type id for "video/mp4" */
    result = BLT_Registry_GetIdForName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        "video/mp4",
        &self->mp4_video_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1("video/mp4 type = %d", self->mp4_video_type_id);

    /* register the type id for BLT_MP4_AUDIO_ES_MIME_TYPE */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        BLT_MP4_AUDIO_ES_MIME_TYPE,
        &self->mp4_audio_es_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1(BLT_MP4_AUDIO_ES_MIME_TYPE " type = %d", self->mp4_audio_es_type_id);

    /* register the type id for BLT_MP4_VIDEO_ES_MIME_TYPE */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        BLT_MP4_VIDEO_ES_MIME_TYPE,
        &self->mp4_video_es_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1(BLT_MP4_VIDEO_ES_MIME_TYPE " type = %d", self->mp4_video_es_type_id);

    /* register the type id for BLT_ISO_BASE_AUDIO_ES_MIME_TYPE */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        BLT_ISO_BASE_AUDIO_ES_MIME_TYPE,
        &self->iso_base_audio_es_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1(BLT_ISO_BASE_AUDIO_ES_MIME_TYPE " type = %d", self->iso_base_audio_es_type_id);

    /* register the type id for BLT_ISO_BASE_VIDEO_ES_MIME_TYPE */
    result = BLT_Registry_RegisterName(
        registry,
        BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
        BLT_ISO_BASE_VIDEO_ES_MIME_TYPE,
        &self->iso_base_video_es_type_id);
    if (BLT_FAILED(result)) return result;
    ATX_LOG_FINE_1(BLT_ISO_BASE_VIDEO_ES_MIME_TYPE " type = %d", self->iso_base_video_es_type_id);

    /* register mime type aliases */
    BLT_Registry_RegisterNameForId(registry, 
                                   BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
                                   "audio/m4a", self->mp4_audio_type_id);
    BLT_Registry_RegisterNameForId(registry, 
                                   BLT_REGISTRY_NAME_CATEGORY_MEDIA_TYPE_IDS,
                                   "video/m4v", self->mp4_video_type_id);

    return BLT_SUCCESS;
}
/*----------------------------------------------------------------------
|   AacDecoderModule_Probe
+---------------------------------------------------------------------*/
BLT_METHOD
AacDecoderModule_Probe(BLT_Module*              _self, 
                       BLT_Core*                core,
                       BLT_ModuleParametersType parameters_type,
                       BLT_AnyConst             parameters,
                       BLT_Cardinal*            match)
{
    AacDecoderModule* self = ATX_SELF_EX(AacDecoderModule, BLT_BaseModule, BLT_Module);
    BLT_COMPILER_UNUSED(core);
    
    switch (parameters_type) {
      case BLT_MODULE_PARAMETERS_TYPE_MEDIA_NODE_CONSTRUCTOR:
        {
            BLT_MediaNodeConstructor* constructor = 
                (BLT_MediaNodeConstructor*)parameters;

            /* the input and output protocols should be PACKET */
            if ((constructor->spec.input.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.input.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_PACKET) ||
                (constructor->spec.output.protocol !=
                 BLT_MEDIA_PORT_PROTOCOL_ANY &&
                 constructor->spec.output.protocol != 
                 BLT_MEDIA_PORT_PROTOCOL_PACKET)) {
                return BLT_FAILURE;
            }

            /* the input type should be BLT_MP4_ES_MIME_TYPE */
            if (constructor->spec.input.media_type->id != 
                self->mp4es_type_id) {
                return BLT_FAILURE;
            } else {
                /* check the object type id */
                BLT_Mp4AudioMediaType* media_type = (BLT_Mp4AudioMediaType*)constructor->spec.input.media_type;
                if (media_type->base.stream_type != BLT_MP4_STREAM_TYPE_AUDIO) return BLT_FAILURE;
                if (media_type->base.format_or_object_type_id != BLT_AAC_OBJECT_TYPE_ID_MPEG2_AAC_LC &&
                    media_type->base.format_or_object_type_id != BLT_AAC_OBJECT_TYPE_ID_MPEG4_AUDIO) {
                    return BLT_FAILURE;
                }
                if (media_type->base.format_or_object_type_id == BLT_AAC_OBJECT_TYPE_ID_MPEG4_AUDIO) {
                    /* check that this is AAC LC */
                    AacDecoderConfig decoder_config;
                    if (BLT_FAILED(AacDecoderConfig_Parse(media_type->decoder_info, media_type->decoder_info_length, &decoder_config))) {
                        return BLT_FAILURE;
                    }
                    if (decoder_config.object_type != BLT_AAC_OBJECT_TYPE_AAC_LC &&
                        decoder_config.object_type != BLT_AAC_OBJECT_TYPE_SBR) {
                        return BLT_FAILURE;
                    }
                }
            }

            /* the output type should be unspecified, or audio/pcm */
            if (!(constructor->spec.output.media_type->id == 
                  BLT_MEDIA_TYPE_ID_AUDIO_PCM) &&
                !(constructor->spec.output.media_type->id ==
                  BLT_MEDIA_TYPE_ID_UNKNOWN)) {
                return BLT_FAILURE;
            }

            /* compute the match level */
            if (constructor->name != NULL) {
                /* we're being probed by name */
                if (ATX_StringsEqual(constructor->name, "AacDecoder")) {
                    /* our name */
                    *match = BLT_MODULE_PROBE_MATCH_EXACT;
                } else {
                    /* not our name */
                    return BLT_FAILURE;
                }
            } else {
                /* we're probed by protocol/type specs only */
                *match = BLT_MODULE_PROBE_MATCH_MAX - 10;
            }

            ATX_LOG_FINE_1("AacDecoderModule::Probe - Ok [%d]", *match);
            return BLT_SUCCESS;
        }    
        break;

      default:
        break;
    }

    return BLT_FAILURE;
}