virtual void OnNackNotification(BLT_DecoderServer_Message::CommandId id, BLT_Result result_code) { ATX_LOG_FINE_2("NACK: %d, result=%d", id, result_code); if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return; jintArray array = (jintArray)m_JniEnv->NewIntArray(2); jint values[2] = {MapCommandId(id), (jint)result_code}; m_JniEnv->SetIntArrayRegion(array, 0, 2, values); m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_NACK, NULL, array); }
/*---------------------------------------------------------------------- | CrossFader_OnPropertyChanged +---------------------------------------------------------------------*/ BLT_VOID_METHOD CrossFader_OnPropertyChanged(ATX_PropertyListenerInstance* instance, ATX_CString name, ATX_PropertyType type, const ATX_PropertyValue* value) { /*CrossFader* fader = (CrossFader*)instance;*/ BLT_COMPILER_UNUSED(instance); BLT_COMPILER_UNUSED(type); ATX_LOG_FINE_2("CrossFader::OnPropertyChanged - name=%s val=%d", name ? name : "*", value ? value->integer : 0); }
/*---------------------------------------------------------------------- | BLT_TcpNetworkStream_Create +---------------------------------------------------------------------*/ BLT_Result BLT_TcpNetworkStream_Create(const char* name, ATX_InputStream** stream) { ATX_Socket* sock; ATX_String hostname = ATX_String_Create(name); ATX_UInt16 port = BLT_TCP_NETWORK_STREAM_DEFAULT_PORT; int sep; ATX_Result result = ATX_SUCCESS; /* default */ *stream = NULL; /* parse the hostname/port */ sep = ATX_String_FindCharFrom(&hostname, ':', 6); if (sep > 0) { /* we have a port number */ int port_long = 0; result = ATX_ParseInteger(name+sep+1, &port_long, ATX_FALSE); if (ATX_FAILED(result)) { ATX_LOG_WARNING("BLT_TcpNetworkStream_Create - invalid port spec"); goto end; } port = (ATX_UInt16)port_long; ATX_String_SetLength(&hostname, sep); } /* create a socket */ result = ATX_TcpClientSocket_Create(&sock); if (ATX_FAILED(result)) goto end; /* connect */ ATX_LOG_FINE_2("BLT_TcpNetworkStream_Create - connecting to %s:%d", ATX_CSTR(hostname), port); result = ATX_Socket_ConnectToHost(sock, ATX_CSTR(hostname), port, BLT_TCP_NETWORK_STREAM_DEFAULT_TIMEOUT); if (ATX_FAILED(result)) { ATX_LOG_WARNING_1("BLT_TcpNetworkStream_Create - failed to connect (%d)", result); goto end; } ATX_LOG_FINE("BLT_TcpNetworkStream_Create - connected"); /* return the input stream */ result = ATX_Socket_GetInputStream(sock, stream); /* release the socket */ ATX_DESTROY_OBJECT(sock); end: ATX_String_Destruct(&hostname); return result; }
virtual void OnStreamPositionNotification(BLT_StreamPosition& position) { ATX_LOG_FINE_2("STREAM-POSITION: %d/%d", (int)position.offset, (int)position.range); if (!m_JniEnv || !m_Delegate || !m_DelegateMethod) return; double fpos = 0.0; if (position.range) { fpos = (double)position.offset/(double)position.range; } jintArray array = (jintArray)m_JniEnv->NewIntArray(2); jint values[2] = {(jint)(fpos*10000.0), (jint)10000}; m_JniEnv->SetIntArrayRegion(array, 0, 2, values); m_JniEnv->CallVoidMethod(m_Delegate, m_DelegateMethod, com_bluetune_player_Player_MESSAGE_TYPE_STREAM_POSITION, NULL, array); }
/*---------------------------------------------------------------------- | 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); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::OnSetInputCommand +---------------------------------------------------------------------*/ void BLT_DecoderServer::OnSetInputCommand(BLT_CString name, BLT_CString type) { BLT_Result result; ATX_LOG_FINE_2("set input (%s / %s)", BLT_SAFE_STRING(name), BLT_SAFE_STRING(type)); result = BLT_Decoder_SetInput(m_Decoder, name, type); // update the state if we were in the STATE_EOS state if (m_State == STATE_EOS) SetState(STATE_STOPPED); UpdateStatus(); SendReply(BLT_DecoderServer_Message::COMMAND_ID_SET_INPUT, result); }
/*---------------------------------------------------------------------- | BLT_DecoderServer::SetState +---------------------------------------------------------------------*/ BLT_Result BLT_DecoderServer::SetState(State state) { // shortcut if (state == m_State) return BLT_SUCCESS; ATX_LOG_FINE_2("state change from %d to %d", m_State, state); m_State = state; // notify the client m_Client->PostMessage( new BLT_DecoderClient_DecoderStateNotificationMessage(state)); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | 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); }
/*---------------------------------------------------------------------- | ATX_HttpMessage_Emit +---------------------------------------------------------------------*/ static ATX_Result ATX_HttpMessage_Emit(const ATX_HttpMessage* message, ATX_OutputStream* stream) { ATX_ListItem* item = ATX_List_GetFirstItem(message->headers); /* output the headers */ while (item) { ATX_HttpHeader* header = ATX_ListItem_GetData(item); if (header && !ATX_String_IsEmpty(&header->name) && !ATX_String_IsEmpty(&header->value)) { ATX_OutputStream_WriteString(stream, ATX_CSTR(header->name)); ATX_OutputStream_Write(stream, ": ", 2, NULL); ATX_OutputStream_WriteLine(stream, ATX_CSTR(header->value)); ATX_LOG_FINE_2("ATX_HttpMessage::Emit - %s: %s", ATX_CSTR(header->name), ATX_CSTR(header->value)); } item = ATX_ListItem_GetNext(item); } return ATX_SUCCESS; }
/*---------------------------------------------------------------------- | ATX_HttpResponse_Parse +---------------------------------------------------------------------*/ static ATX_Result ATX_HttpResponse_Parse(ATX_HttpResponse* response, ATX_InputStream* stream) { char buffer[ATX_HTTP_MAX_LINE_SIZE+1]; char* line = buffer; char* find; ATX_Boolean header_pending = ATX_FALSE; ATX_String header_name = ATX_EMPTY_STRING; ATX_String header_value = ATX_EMPTY_STRING; ATX_Result result; /* get the first line from the stream */ result = ATX_InputStream_ReadLine(stream, line, sizeof(buffer), NULL); if (ATX_FAILED(result)) return result; /* get the protocol */ find = (char*)ATX_Http_FindChar(line, ' '); if (find == NULL) { return ATX_ERROR_INVALID_SYNTAX; } *find = '\0'; ATX_String_Assign(&response->base.protocol, line); /* get the status code */ line = (char*)ATX_Http_SkipWhitespace(find+1); find = (char*)ATX_Http_FindChar(line, ' '); if (find == NULL) { return ATX_ERROR_INVALID_SYNTAX; } *find = '\0'; if (ATX_StringLength(line) != 3) { return ATX_ERROR_INVALID_SYNTAX; } { int i; response->status_code = 0; for (i=0; i<3; i++) { if (line[i] < '0' || line[i] > '9') { return ATX_ERROR_INVALID_SYNTAX; } response->status_code *= 10; response->status_code += line[i]-'0'; } } /* the rest is the reason phrase */ line = (char*)ATX_Http_SkipWhitespace(find+1); ATX_String_Assign(&response->reason_phrase, line); /* parse headers until an empty line or end of stream */ do { /* read a line */ result = ATX_InputStream_ReadLine(stream, line, sizeof(buffer), NULL); if (ATX_FAILED(result)) break; /* stop if line is empty */ if (line[0] == '\0' || line[0] == '\r' || line[0] == '\n') { if (header_pending) { ATX_String_TrimWhitespace(&header_value); ATX_HttpMessage_SetHeader((ATX_HttpMessage*)response, ATX_CSTR(header_name), ATX_CSTR(header_value)); ATX_LOG_FINE_2("ATX_HttpResponse::Parse - %s: %s", ATX_CSTR(header_name), ATX_CSTR(header_value)); } break; } /* process the line */ if ((line[0] == ' ' || line[0] == '\t') && header_pending) { /* this is a line continuation */ ATX_String_Append(&header_value, line+1); } else { /* this is a new header */ const char* name; const char* value; /* add the pending header to the list */ if (header_pending) { ATX_String_TrimWhitespace(&header_value); ATX_HttpMessage_SetHeader((ATX_HttpMessage*)response, ATX_CSTR(header_name), ATX_CSTR(header_value)); ATX_LOG_FINE_2("ATX_HttpResponse::Parse - %s: %s", ATX_CSTR(header_name), ATX_CSTR(header_value)); } /* parse header name */ name = ATX_Http_SkipWhitespace(line); value = ATX_Http_FindChar(name, ':'); ATX_String_AssignN(&header_name, name, (ATX_Size)(value-name)); value = ATX_Http_SkipWhitespace(value+1); ATX_String_Assign(&header_value, value); /* don't add the header now, it could be continued */ header_pending = ATX_TRUE; } } while(ATX_SUCCEEDED(result)); /* keep a reference to the stream */ response->base.body = stream; ATX_REFERENCE_OBJECT(stream); /* cleanup */ ATX_String_Destruct(&header_name); ATX_String_Destruct(&header_value); return ATX_SUCCESS; }
/*---------------------------------------------------------------------- | DcfParser_ParseV2Header +---------------------------------------------------------------------*/ static BLT_Result DcfParser_ParseV2Header(DcfParser* self, ATX_InputStream* stream) { /* rewind the byte stream */ ATX_InputStream_Seek(stream, 0); /* parse the atoms from the stream */ AP4_ByteStream* mp4_stream = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream); AP4_AtomParent atoms; AP4_Result result = AP4_DefaultAtomFactory::Instance.CreateAtomsFromStream(*mp4_stream, atoms); mp4_stream->Release(); AP4_ByteStream* decrypting_stream = NULL; AP4_ContainerAtom* odrm = dynamic_cast<AP4_ContainerAtom*>(atoms.GetChild(AP4_ATOM_TYPE_ODRM)); if (odrm) { AP4_OdheAtom* odhe = dynamic_cast<AP4_OdheAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODHE)); AP4_OddaAtom* odda = dynamic_cast<AP4_OddaAtom*>(odrm->GetChild(AP4_ATOM_TYPE_ODDA)); if (odhe && odda) { const char* content_id = ""; /* get the content ID */ AP4_OhdrAtom* ohdr = dynamic_cast<AP4_OhdrAtom*>(odhe->GetChild(AP4_ATOM_TYPE_OHDR)); if (ohdr) { content_id = ohdr->GetContentId().GetChars(); } /* get the content key */ NPT_DataBuffer key; result = DcfParser_GetContentKey(self, content_id, key); if (BLT_FAILED(result)) { ATX_LOG_FINE_2("GetKeyForContent(%s) returned %d", content_id, result); return BLT_ERROR_NO_MEDIA_KEY; } /* create the decrypting stream */ result = AP4_OmaDcfAtomDecrypter::CreateDecryptingStream(*odrm, key.GetData(), key.GetDataSize(), self->cipher_factory, decrypting_stream); if (AP4_SUCCEEDED(result)) { /* update the content type */ ATX_CopyStringN(self->input.content_type, odhe->GetContentType().GetChars(), sizeof(self->input.content_type)); /* update the encrypted size */ self->input.encrypted_size = odda->GetEncryptedDataLength(); } } } /* check that we have found what we needed in the atoms */ if (decrypting_stream == NULL) return BLT_ERROR_INVALID_MEDIA_FORMAT; /* update the output size */ AP4_LargeSize plaintext_size = 0; if (AP4_SUCCEEDED(decrypting_stream->GetSize(plaintext_size))) { self->output.size = plaintext_size; } else { self->output.size = self->input.encrypted_size; } /* create a reverse adapter */ result = AP4_ByteStream_To_ATX_InputStream_Adapter_Create(decrypting_stream, &self->output.stream); decrypting_stream->Release(); return BLT_SUCCESS; }
/*---------------------------------------------------------------------- | 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; }
/*---------------------------------------------------------------------- | OsxAudioUnitsOutput_MapDeviceIndex +---------------------------------------------------------------------*/ static BLT_Result OsxAudioUnitsOutput_MapDeviceName(const char* name, AudioDeviceID* device_id) { OSStatus err; BLT_Result result = BLT_ERROR_NO_SUCH_DEVICE; UInt32 prop_size = 0; AudioDeviceID* devices = NULL; unsigned int device_count = 0; ATX_UInt32 device_index = 0; unsigned int device_selector = 1; char* device_name = NULL; unsigned int i; /* setup a default value */ *device_id = 0; /* check the parameters */ if (name[0] == '\0') return BLT_ERROR_NO_SUCH_DEVICE; /* parse the name */ if (name[0] == '#') { ++name; ATX_LOG_FINE_1("device name = %s", name); } else { if (BLT_FAILED(ATX_ParseInteger32U(name, &device_index, ATX_FALSE))) { return BLT_ERROR_NO_SUCH_DEVICE; } ATX_LOG_FINE_1("device index = %d", device_index); name = NULL; /* 0 means default */ if (device_index == 0) { /* look at the device's streams */ AudioDeviceID default_device_id = 0; prop_size = sizeof(AudioDeviceID); err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &prop_size, &default_device_id); if (err == noErr) { prop_size = 0; err = AudioDeviceGetPropertyInfo(default_device_id, 0, FALSE, kAudioDevicePropertyStreams, &prop_size, NULL); if (err == noErr) { ATX_LOG_FINE_1("device has %d streams", (int)prop_size/4); } } return BLT_SUCCESS; } } /* ask how many devices exist */ err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &prop_size, NULL); if (err != noErr) { ATX_LOG_FINE_1("AudioHardwareGetPropertyInfo failed (%d)", (int)err); return 0; } device_count = prop_size/sizeof(AudioDeviceID); ATX_LOG_FINE_1("found %d devices", device_count); /* allocate memory for the array */ devices = (AudioDeviceID*)ATX_AllocateZeroMemory(sizeof(AudioDeviceID) * device_count); if (devices == NULL) return 0; /* retrieve the list of device IDs */ err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &prop_size, devices); if (err != noErr) { ATX_LOG_FINE_1("AudioHardwareGetProperty(kAudioHardwarePropertyDevices) failed (%d)", (int)err); return 0; } /* find the device ID we want */ for (i=0; i<device_count; i++) { /* get the device name */ err = AudioDeviceGetPropertyInfo(devices[i], 0, false, kAudioDevicePropertyDeviceName, &prop_size, NULL); if (err == noErr) { if (device_name) ATX_FreeMemory(device_name); device_name = (char*)ATX_AllocateZeroMemory(prop_size+1); err = AudioDeviceGetProperty(devices[i], 0, false, kAudioDevicePropertyDeviceName, &prop_size, device_name); if (err == noErr) { ATX_LOG_FINE_2("device name [%d] = %s", i, device_name); } /* cleanup the string */ device_name[prop_size] = '\0'; /* NULL-terminate the string */ if (prop_size > 0) { unsigned int x; for (x=prop_size-1; x; x--) { if (device_name[x] == ' ') { device_name[x] = '\0'; } else if (device_name[x]) { break; } } } } else { continue; } /* look at the device's streams */ prop_size = 0; err = AudioDeviceGetPropertyInfo(devices[i], 0, FALSE, kAudioDevicePropertyStreams, &prop_size, NULL); if (err != noErr || prop_size == 0) { ATX_LOG_FINE("skipping device (not an output)"); continue; } if (name) { /* look for a match by name */ if (ATX_StringsEqual(device_name, name)) { *device_id = devices[i]; ATX_LOG_FINE("device selected as output"); result = BLT_SUCCESS; break; } } else { /* look for a match by index */ if (device_selector++ == device_index) { *device_id = devices[i]; ATX_LOG_FINE("device selected as output"); result = BLT_SUCCESS; break; } } } if (device_name) ATX_FreeMemory(device_name); if (devices) ATX_FreeMemory(devices); return result; }