bool Session::initialize() { // Get URN's wich are supported by this addon if (!license_type_.empty()) { GetSupportedDecrypterURN(dashtree_.adp_pssh_); xbmc->Log(ADDON::LOG_DEBUG, "Supported URN: %s", dashtree_.adp_pssh_.first.c_str()); } // Open mpd file size_t paramPos = mpdFileURL_.find('?'); dashtree_.base_url_ = (paramPos == std::string::npos)? mpdFileURL_:mpdFileURL_.substr(0, paramPos); paramPos = dashtree_.base_url_.find_last_of('/', dashtree_.base_url_.length()); if (paramPos == std::string::npos) { xbmc->Log(ADDON::LOG_ERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str()); return false; } dashtree_.base_url_.resize(paramPos + 1); if (!dashtree_.open(mpdFileURL_.c_str()) || dashtree_.empty()) { xbmc->Log(ADDON::LOG_ERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str()); return false; } xbmc->Log(ADDON::LOG_INFO, "Successfully parsed .mpd file. Download speed: %0.4f Bytes/s", dashtree_.download_speed_); if (dashtree_.encryptionState_ == dash::DASHTree::ENCRYTIONSTATE_ENCRYPTED) { xbmc->Log(ADDON::LOG_ERROR, "Unable to handle decryption. Unsupported!"); return false; } uint32_t min_bandwidth(0), max_bandwidth(0); { int buf; xbmc->GetSetting("MINBANDWIDTH", (char*)&buf); min_bandwidth = buf; xbmc->GetSetting("MAXBANDWIDTH", (char*)&buf); max_bandwidth = buf; } // create SESSION::STREAM objects. One for each AdaptationSet unsigned int i(0); const dash::DASHTree::AdaptationSet *adp; for (std::vector<STREAM*>::iterator b(streams_.begin()), e(streams_.end()); b != e; ++b) SAFE_DELETE(*b); streams_.clear(); while ((adp = dashtree_.GetAdaptationSet(i++))) { streams_.push_back(new STREAM(dashtree_, adp->type_)); STREAM &stream(*streams_.back()); stream.stream_.prepare_stream(adp, width_, height_, min_bandwidth, max_bandwidth); switch (adp->type_) { case dash::DASHTree::VIDEO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_VIDEO; break; case dash::DASHTree::AUDIO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_AUDIO; break; case dash::DASHTree::TEXT: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_TELETEXT; break; default: break; } stream.info_.m_pID = i; strcpy(stream.info_.m_language, adp->language_.c_str()); UpdateStream(stream); } // Try to initialize an SingleSampleDecryptor #if 1 if (dashtree_.encryptionState_) { if (dashtree_.protection_key_.size()!=16 || license_data_.empty()) return false; uint8_t ld[1024]; unsigned int ld_size(1024); b64_decode(license_data_.c_str(), license_data_.size(), ld, ld_size); const uint8_t *uuid((uint8_t*)strstr((const char*)ld, "{UUID}")); unsigned int license_size = uuid ? ld_size + 36 -6: ld_size; //Build up proto header AP4_DataBuffer init_data; init_data.Reserve(512); uint8_t *protoptr(init_data.UseData()); *protoptr++ = 18; //id=16>>3=2, type=2(flexlen) *protoptr++ = 16; //length of key memcpy(protoptr, dashtree_.protection_key_.data(), 16); protoptr += 16; //----------- *protoptr++ = 34;//id=32>>3=4, type=2(flexlen) do { *protoptr++ = static_cast<uint8_t>(license_size & 127); license_size >>= 7; if (license_size) *(protoptr - 1) |= 128; else break; } while (1); if (uuid) { static const uint8_t hexmap[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; memcpy(protoptr, ld, uuid - ld); protoptr += uuid - ld; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[3]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[3]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[2]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[2]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[1]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[1]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[0]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[0]) & 15]; *protoptr++ = '-'; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[5]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[5]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[4]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[4]) & 15]; *protoptr++ = '-'; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[7]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[7]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[6]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[6]) & 15]; *protoptr++ = '-'; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[8]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[8]) & 15]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[9]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[9]) & 15]; *protoptr++ = '-'; for (i = 10; i < 16; ++i) { *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[i]) >> 4]; *protoptr++ = hexmap[(uint8_t)(dashtree_.protection_key_.data()[i]) & 15]; } unsigned int sizeleft = ld_size - ((uuid - ld) + 6); memcpy(protoptr, uuid+6, sizeleft); protoptr += sizeleft; } else { memcpy(protoptr, ld, ld_size); protoptr += ld_size; } init_data.SetDataSize(protoptr - init_data.UseData()); return (single_sample_decryptor_ = CreateSingleSampleDecrypter(init_data))!=0; }
bool Session::initialize() { // Get URN's wich are supported by this addon if (!license_type_.empty()) GetSupportedDecrypterURN(dashtree_.adp_pssh_); // Open mpd file const char* delim(strrchr(mpdFileURL_.c_str(), '/')); if (!delim) { xbmc->Log(ADDON::LOG_ERROR, "Invalid mpdURL: / expected (%s)", mpdFileURL_.c_str()); return false; } dashtree_.base_url_ = std::string(mpdFileURL_.c_str(), (delim - mpdFileURL_.c_str()) + 1); if (!dashtree_.open(mpdFileURL_.c_str()) || dashtree_.empty()) { xbmc->Log(ADDON::LOG_ERROR, "Could not open / parse mpdURL (%s)", mpdFileURL_.c_str()); return false; } xbmc->Log(ADDON::LOG_INFO, "Successfully parsed .mpd file. Download speed: %0.4f Bytes/s", dashtree_.download_speed_); if (dashtree_.encryptionState_ == dash::DASHTree::ENCRYTIONSTATE_ENCRYPTED) { xbmc->Log(ADDON::LOG_ERROR, "Unable to handle decryption. Unsupported!"); return false; } uint32_t min_bandwidth(0), max_bandwidth(0); { int buf; xbmc->GetSetting("MINBANDWIDTH", (char*)&buf); min_bandwidth = buf; xbmc->GetSetting("MAXBANDWIDTH", (char*)&buf); max_bandwidth = buf; } // create SESSION::STREAM objects. One for each AdaptationSet unsigned int i(0); const dash::DASHTree::AdaptationSet *adp; for (std::vector<STREAM*>::iterator b(streams_.begin()), e(streams_.end()); b != e; ++b) SAFE_DELETE(*b); streams_.clear(); while ((adp = dashtree_.GetAdaptationSet(i++))) { streams_.push_back(new STREAM(dashtree_, adp->type_)); STREAM &stream(*streams_.back()); stream.stream_.prepare_stream(adp, width_, height_, min_bandwidth, max_bandwidth); const dash::DASHTree::Representation *rep(stream.stream_.getRepresentation()); stream.info_.m_Width = rep->width_; stream.info_.m_Height = rep->height_; stream.info_.m_Aspect = rep->aspect_; stream.info_.m_pID = i; switch (adp->type_) { case dash::DASHTree::VIDEO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_VIDEO; break; case dash::DASHTree::AUDIO: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_AUDIO; break; case dash::DASHTree::TEXT: stream.info_.m_streamType = INPUTSTREAM_INFO::TYPE_TELETEXT; break; default: break; } // we currently use only the first track! std::string::size_type pos = rep->codecs_.find(","); if (pos == std::string::npos) pos = rep->codecs_.size(); strncpy(stream.info_.m_codecInternalName, rep->codecs_.c_str(), pos); stream.info_.m_codecInternalName[pos] = 0; if (rep->codecs_.find("mp4a") == 0) strcpy(stream.info_.m_codecName, "aac"); else if (rep->codecs_.find("ec-3") == 0 || rep->codecs_.find("ac-3") == 0) strcpy(stream.info_.m_codecName, "eac3"); else if (rep->codecs_.find("avc") == 0) strcpy(stream.info_.m_codecName, "h264"); else if (rep->codecs_.find("hevc") == 0) strcpy(stream.info_.m_codecName, "hevc"); stream.info_.m_FpsRate = rep->fpsRate_; stream.info_.m_FpsScale = rep->fpsScale_; stream.info_.m_SampleRate = rep->samplingRate_; stream.info_.m_Channels = rep->channelCount_; stream.info_.m_Bandwidth = rep->bandwidth_; strcpy(stream.info_.m_language, adp->language_.c_str()); } // Try to initialize an SingleSampleDecryptor if (dashtree_.encryptionState_) { AP4_DataBuffer init_data; if (dashtree_.adp_pssh_.second == "FILE") { std::string strkey(dashtree_.adp_pssh_.first.substr(9)); while (size_t pos = strkey.find('-') != std::string::npos) strkey.erase(pos, 1); if (strkey.size() != 32) { xbmc->Log(ADDON::LOG_ERROR, "Key system mismatch (%s)!", dashtree_.adp_pssh_.first.c_str()); return false; } unsigned char key_system[16]; AP4_ParseHex(strkey.c_str(), key_system, 16); Session::STREAM *stream(streams_[0]); stream->enabled = true; stream->stream_.start_stream(0); stream->stream_.select_stream(true); stream->input_ = new AP4_DASHStream(&stream->stream_); stream->input_file_ = new AP4_File(*stream->input_, AP4_DefaultAtomFactory::Instance, true); AP4_Movie* movie = stream->input_file_->GetMovie(); if (movie == NULL) { xbmc->Log(ADDON::LOG_ERROR, "No MOOV in stream!"); stream->disable(); return false; } AP4_Array<AP4_PsshAtom*>& pssh = movie->GetPsshAtoms(); for (unsigned int i = 0; !init_data.GetDataSize() && i < pssh.ItemCount(); i++) { if (memcmp(pssh[i]->GetSystemId(), key_system, 16) == 0) init_data.AppendData(pssh[i]->GetData().GetData(), pssh[i]->GetData().GetDataSize()); } if (!init_data.GetDataSize()) { xbmc->Log(ADDON::LOG_ERROR, "Could not extract license from video stream (PSSH not found)"); stream->disable(); return false; } stream->disable(); } else { init_data.SetBufferSize(1024); unsigned int init_data_size(1024); b64_decode(dashtree_.pssh_.second.data(), dashtree_.pssh_.second.size(), init_data.UseData(), init_data_size); init_data.SetDataSize(init_data_size); } return (single_sample_decryptor_ = CreateSingleSampleDecrypter(init_data))!=0; } return true; }