Example #1
0
/*----------------------------------------------------------------------
|   PLT_MediaItemResource::PLT_MediaItemResource
+---------------------------------------------------------------------*/
PLT_MediaItemResource::PLT_MediaItemResource()
{
    m_Uri             = "";
    m_ProtocolInfo    = PLT_ProtocolInfo();
    m_Duration        = (NPT_UInt32)-1;
    m_Size            = (NPT_LargeSize)-1;
    m_Protection      = "";
    m_Bitrate         = (NPT_UInt32)-1;
    m_BitsPerSample   = (NPT_UInt32)-1;
    m_SampleFrequency = (NPT_UInt32)-1;
    m_NbAudioChannels = (NPT_UInt32)-1;
    m_Resolution      = "";
    m_ColorDepth      = (NPT_UInt32)-1;
}
Example #2
0
int CUPnPPlayer::PlayFile(const CFileItem& file, const CPlayerOptions& options, CGUIDialogBusy*& dialog, XbmcThreads::EndTime& timeout)
{
  CFileItem item(file);
  NPT_Reference<CThumbLoader> thumb_loader;
  NPT_Reference<PLT_MediaObject> obj;
  NPT_String path(file.GetPath().c_str());
  NPT_String tmp, resource;
  EMediaControllerQuirks quirks = EMEDIACONTROLLERQUIRKS_NONE;

  NPT_CHECK_POINTER_LABEL_SEVERE(m_delegate, failed);

  if (file.IsVideoDb())
    thumb_loader = NPT_Reference<CThumbLoader>(new CVideoThumbLoader());
  else if (item.IsMusicDb())
    thumb_loader = NPT_Reference<CThumbLoader>(new CMusicThumbLoader());

  obj = BuildObject(item, path, false, thumb_loader, NULL, CUPnP::GetServer(), UPnPPlayer);
  if(obj.IsNull()) goto failed;

  NPT_CHECK_LABEL_SEVERE(PLT_Didl::ToDidl(*obj, "", tmp), failed_todidl);
  tmp.Insert(didl_header, 0);
  tmp.Append(didl_footer);

  quirks = GetMediaControllerQuirks(m_delegate->m_device.AsPointer());
  if (quirks & EMEDIACONTROLLERQUIRKS_X_MKV)
  {
    for (NPT_Cardinal i=0; i< obj->m_Resources.GetItemCount(); i++) {
      if (obj->m_Resources[i].m_ProtocolInfo.GetContentType().Compare("video/x-matroska") == 0) {
        CLog::Log(LOGDEBUG, "CUPnPPlayer::PlayFile(%s): applying video/x-mkv quirk", file.GetPath().c_str());
        NPT_String protocolInfo = obj->m_Resources[i].m_ProtocolInfo.ToString();
        protocolInfo.Replace(":video/x-matroska:", ":video/x-mkv:");
        obj->m_Resources[i].m_ProtocolInfo = PLT_ProtocolInfo(protocolInfo);
      }
    }
  }

  /* The resource uri's are stored in the Didl. We must choose the best resource
   * for the playback device */
  NPT_Cardinal res_index;
  NPT_CHECK_LABEL_SEVERE(m_control->FindBestResource(m_delegate->m_device, *obj, res_index), failed_findbestresource);

  // get the transport info to evaluate the TransportState to be able to
  // determine whether we first need to call Stop()
  timeout.Set(timeout.GetInitialTimeoutValue());
  NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
                                                     , m_delegate->m_instance
                                                     , m_delegate), failed_gettransportinfo);
  NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed_gettransportinfo);

  if (m_delegate->m_trainfo.cur_transport_state != "NO_MEDIA_PRESENT" &&
      m_delegate->m_trainfo.cur_transport_state != "STOPPED")
  {
    timeout.Set(timeout.GetInitialTimeoutValue());
    NPT_CHECK_LABEL_SEVERE(m_control->Stop(m_delegate->m_device
                                           , m_delegate->m_instance
                                           , m_delegate), failed_stop);
    NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_stop);
    NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_stop);
  }


  timeout.Set(timeout.GetInitialTimeoutValue());
  NPT_CHECK_LABEL_SEVERE(m_control->SetAVTransportURI(m_delegate->m_device
                                                    , m_delegate->m_instance
                                                    , obj->m_Resources[res_index].m_Uri
                                                    , (const char*)tmp
                                                    , m_delegate), failed_setavtransporturi);
  NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_setavtransporturi);
  NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_setavtransporturi);

  timeout.Set(timeout.GetInitialTimeoutValue());
  NPT_CHECK_LABEL_SEVERE(m_control->Play(m_delegate->m_device
                                       , m_delegate->m_instance
                                       , "1"
                                       , m_delegate), failed_play);
  NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_resevent, timeout, dialog), failed_play);
  NPT_CHECK_LABEL_SEVERE(m_delegate->m_resstatus, failed_play);


  /* wait for PLAYING state */
  timeout.Set(timeout.GetInitialTimeoutValue());
  do {
    NPT_CHECK_LABEL_SEVERE(m_control->GetTransportInfo(m_delegate->m_device
                                                     , m_delegate->m_instance
                                                     , m_delegate), failed_waitplaying);


    { CSingleLock lock(m_delegate->m_section);
      if(m_delegate->m_trainfo.cur_transport_state == "PLAYING"
      || m_delegate->m_trainfo.cur_transport_state == "PAUSED_PLAYBACK")
        break;

      if(m_delegate->m_trainfo.cur_transport_state  == "STOPPED"
      && m_delegate->m_trainfo.cur_transport_status != "OK")
      {
        CLog::Log(LOGERROR, "UPNP: CUPnPPlayer::OpenFile - remote player signalled error %s", file.GetPath().c_str());
        return NPT_FAILURE;
      }
    }

    NPT_CHECK_LABEL_SEVERE(WaitOnEvent(m_delegate->m_traevnt, timeout, dialog), failed_waitplaying);

  } while(!timeout.IsTimePast());

  if(options.starttime > 0)
  {
    /* many upnp units won't load file properly until after play (including xbmc) */
    NPT_CHECK_LABEL(m_control->Seek(m_delegate->m_device
                                    , m_delegate->m_instance
                                    , "REL_TIME"
                                    , PLT_Didl::FormatTimeStamp((NPT_UInt32)options.starttime)
                                    , m_delegate), failed_seek);
  }

  return NPT_SUCCESS;
failed_todidl:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to serialize item into DIDL-Lite", file.GetPath().c_str());
  return NPT_FAILURE;
failed_findbestresource:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to find a matching resource", file.GetPath().c_str());
  return NPT_FAILURE;
failed_gettransportinfo:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s): call to GetTransportInfo failed", file.GetPath().c_str());
  return NPT_FAILURE;
failed_stop:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to stop current playback", file.GetPath().c_str());
  return NPT_FAILURE;
failed_setavtransporturi:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to set the playback URI", file.GetPath().c_str());
  return NPT_FAILURE;
failed_play:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to start playback", file.GetPath().c_str());
  return NPT_FAILURE;
failed_waitplaying:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to wait for PLAYING state", file.GetPath().c_str());
  return NPT_FAILURE;
failed_seek:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed to seek to start offset", file.GetPath().c_str());
  return NPT_FAILURE;
failed:
  CLog::Log(LOGERROR, "CUPnPPlayer::PlayFile(%s) failed", file.GetPath().c_str());
  return NPT_FAILURE;
}
Example #3
0
/*----------------------------------------------------------------------
|   CUPnPDirectory::GetDirectory
+---------------------------------------------------------------------*/
bool CUPnPDirectory::GetResource(const CURL& path, CFileItem &item)
{
    if(path.GetProtocol() != "upnp")
        return false;

    CUPnP* upnp = CUPnP::GetInstance();
    if(!upnp)
        return false;

    CStdString uuid   = path.GetHostName();
    CStdString object = path.GetFileName();
    object.TrimRight("/");
    CURL::Decode(object);

    PLT_DeviceDataReference device;
    if(!FindDeviceWait(upnp, uuid.c_str(), device))
        return false;

    PLT_MediaObjectListReference list;
    if (NPT_FAILED(upnp->m_MediaBrowser->BrowseSync(device, object.c_str(), list, true)))
        return false;

    PLT_MediaObjectList::Iterator entry = list->GetFirstItem();
    if (entry == 0)
        return false;

    PLT_MediaItemResource resource;

    // look for a resource with "xbmc-get" protocol
    // if we can't find one, keep the first resource
    if(NPT_FAILED(NPT_ContainerFind((*entry)->m_Resources,
                                    CProtocolFinder("xbmc-get"), resource))) {
        if((*entry)->m_Resources.GetItemCount())
            resource = (*entry)->m_Resources[0];
        else
            return false;
    }

    // store original path so we remember it
    item.SetProperty("original_listitem_url",  item.GetPath());
    item.SetProperty("original_listitem_mime", item.GetMimeType(false));

    // if it's an item, path is the first url to the item
    // we hope the server made the first one reachable for us
    // (it could be a format we dont know how to play however)
    item.SetPath((const char*) resource.m_Uri);

    // look for content type in protocol info
    if (resource.m_ProtocolInfo.IsValid()) {
        CLog::Log(LOGDEBUG, "CUPnPDirectory::GetResource - resource protocol info '%s'",
                  (const char*)(resource.m_ProtocolInfo.ToString()));

        if (resource.m_ProtocolInfo.GetContentType().Compare("application/octet-stream") != 0) {
            item.SetMimeType((const char*)resource.m_ProtocolInfo.GetContentType());
        }
    } else {
        CLog::Log(LOGERROR, "CUPnPDirectory::GetResource - invalid protocol info '%s'",
                  (const char*)(resource.m_ProtocolInfo.ToString()));
    }

    // look for subtitles
    unsigned subs = 0;
    for(unsigned r = 0; r < (*entry)->m_Resources.GetItemCount(); r++)
    {
        PLT_MediaItemResource& res  = (*entry)->m_Resources[r];
        PLT_ProtocolInfo&      info = res.m_ProtocolInfo;
        static const char* allowed[] = { "text/srt"
                                         , "text/ssa"
                                         , "text/sub"
                                         , "text/idx"
                                       };
        for(unsigned type = 0; type < sizeof(allowed)/sizeof(allowed[0]); type++)
        {
            if(info.Match(PLT_ProtocolInfo("*", "*", allowed[type], "*")))
            {
                CStdString prop;
                prop.Format("upnp:subtitle:%d", ++subs);
                item.SetProperty(prop, (const char*)res.m_Uri);
                break;
            }
        }
    }

    return true;
}
Example #4
0
/*----------------------------------------------------------------------
|   PLT_MediaObject::FromDidl
+---------------------------------------------------------------------*/
NPT_Result
PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry)
{
    NPT_String str, xml;
    NPT_Array<NPT_XmlElementNode*> children;
    NPT_Result res;

    // check if item is restricted (is default true?)
    if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str, "", 5))) {
        m_Restricted = PLT_Service::IsTrue(str);
    }
    
    // read non-required elements
    PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc, 256);
    PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc, 256);
    
    // parse date and make sure it's valid
    NPT_String parsed_date;
    for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
        NPT_DateTime date;
        if (NPT_SUCCEEDED(date.FromString(m_Date, (NPT_DateTime::Format)format))) {
            parsed_date = date.ToString((NPT_DateTime::Format)format);
            break;
        }
    }
    m_Date = parsed_date;

    res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID);
    NPT_CHECK_SEVERE(res);

    res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID);
    NPT_CHECK_SEVERE(res);

    PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID);

    res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc);
    NPT_CHECK_SEVERE(res);
    
    res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp);
    NPT_CHECK_SEVERE(res);
    
    // DLNA 7.3.17.3 max bytes for dc:title and upnp:class is 256 bytes
    m_Title = m_Title.SubString(0, 256);    
    m_ObjectClass.type =  m_ObjectClass.type.SubString(0, 256);

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp);
    m_People.artists.FromDidl(children);
    
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "author", didl_namespace_upnp);
    m_People.authors.FromDidl(children);
    
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "actors", didl_namespace_upnp);
    m_People.actors.FromDidl(children);

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "director", didl_namespace_upnp);
    m_People.directors.FromDidl(children);

    PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp, 256);
    PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp);
    NPT_UInt32 value;
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_Recorded.episode_number = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp);
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        if (children[i]->GetText()) {
            m_Affiliation.genres.Add(children[i]->GetText()->SubString(0, 256));
        }
    }
    
    PLT_XmlHelper::GetChildText(entry, "description", m_Description.description, didl_namespace_dc);
    PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "icon", m_Description.icon_uri, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "rating", m_Description.rating, didl_namespace_upnp);
	PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp);
    
    // album arts
    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "albumArtURI", didl_namespace_upnp);
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        if (children[i]->GetText()) {
            PLT_AlbumArtInfo info;
            info.uri = children[i]->GetText()->SubString(0, 1024);
            PLT_XmlHelper::GetAttribute(children[i], "profileID", info.dlna_profile, didl_namespace_dlna);
            m_ExtraInfo.album_arts.Add(info);
        }
    }
    
    PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_MiscInfo.original_track_number = value;

    PLT_XmlHelper::GetChildText(entry, "lastPlaybackPosition", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_MiscInfo.last_position = value;

    PLT_XmlHelper::GetChildText(entry, "lastPlaybackTime", m_MiscInfo.last_time, didl_namespace_dc, 256);
    NPT_String parsed_last_time;
    for (int format=0; format<=NPT_DateTime::FORMAT_RFC_1036; format++) {
        NPT_DateTime date;
        if (NPT_SUCCEEDED(date.FromString(m_MiscInfo.last_time, (NPT_DateTime::Format)format))) {
            parsed_last_time = date.ToString((NPT_DateTime::Format)format);
            break;
        }
    }
    m_MiscInfo.last_time = parsed_last_time;

    PLT_XmlHelper::GetChildText(entry, "playbackCount", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = -1;
    m_MiscInfo.play_count = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "res");
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        PLT_MediaItemResource resource;

        // extract url
        if (children[i]->GetText() == NULL) {
            NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_Uri = children[i]->GetText()->SubString(0, 1024);

            // basic uri validation, ignoring scheme (could be rtsp)
            NPT_HttpUrl url(resource.m_Uri, true);
            if (!url.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri);
                continue;
            }
        }

        // extract protocol info
        NPT_String protocol_info;
        res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info, "", 256);
        if (NPT_FAILED(res)) {
            NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info);
            if (!resource.m_ProtocolInfo.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info);
            }
        }

        // extract known attributes
        PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection, "", 256);
        PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution, "", 256);

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str, "", 256))) {
            if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1;
        }

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str, "", 256))) {
            if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) {
                // if error while converting, ignore and set to -1 to indicate we don't know the duration
                resource.m_Duration = (NPT_UInt32)-1;
                PLT_XmlHelper::RemoveAttribute(children[i], "duration");
            } else {
                // DLNA: reformat duration in case it was not compliant
                str = PLT_Didl::FormatTimeStamp(resource.m_Duration);
                PLT_XmlHelper::SetAttribute(children[i], "duration", str); 
            }
        }    
        m_Resources.Add(resource);
    }

    // re serialize the entry didl as a we might need to pass it to a renderer
    // we may have modified the tree to "fix" issues, so as not to break a renderer
    // (don't write xml prefix as this didl could be part of a larger document)
    //res = PLT_XmlHelper::Serialize(*entry, xml, false);
    m_Didl = "";
    res = ToDidl(PLT_FILTER_MASK_ALL, m_Didl);
    NPT_CHECK_SEVERE(res);
    
    m_Didl = didl_header + m_Didl + didl_footer;    
    return NPT_SUCCESS;
}
/*----------------------------------------------------------------------
|   CUPnPDirectory::GetDirectory
+---------------------------------------------------------------------*/
bool
CUPnPDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
{
    CUPnP* upnp = CUPnP::GetInstance();

    /* upnp should never be cached, it has internal cache */
    items.SetCacheToDisc(CFileItemList::CACHE_NEVER);

    // start client if it hasn't been done yet
    bool client_started = upnp->IsClientStarted();
    upnp->StartClient();

    // We accept upnp://devuuid/[item_id/]
    NPT_String path = strPath.c_str();
    if (!path.StartsWith("upnp://", true)) {
        return false;
    }

    if (path.Compare("upnp://", true) == 0) {
        // root -> get list of devices
        const NPT_Lock<PLT_DeviceDataReferenceList>& devices = upnp->m_MediaBrowser->GetMediaServers();
        NPT_List<PLT_DeviceDataReference>::Iterator device = devices.GetFirstItem();
        while (device) {
            NPT_String name = (*device)->GetFriendlyName();
            NPT_String uuid = (*device)->GetUUID();

            CFileItemPtr pItem(new CFileItem((const char*)name));
            pItem->m_strPath = (const char*) "upnp://" + uuid + "/";
            pItem->m_bIsFolder = true;
            pItem->SetThumbnailImage((const char*)(*device)->GetIconUrl("image/jpeg"));

            items.Add(pItem);

            ++device;
        }
    } else {
        if (!path.EndsWith("/")) path += "/";

        // look for nextslash
        int next_slash = path.Find('/', 7);

        NPT_String uuid = (next_slash==-1)?path.SubString(7):path.SubString(7, next_slash-7);
        NPT_String object_id = (next_slash==-1)?"":path.SubString(next_slash+1);
        object_id.TrimRight("/");
        if (object_id.GetLength()) {
            CStdString tmp = (char*) object_id;
            CUtil::URLDecode(tmp);
            object_id = tmp;
        }

        // look for device in our list
        // (and wait for it to respond for 5 secs if we're just starting upnp client)
        NPT_TimeStamp watchdog;
        NPT_System::GetCurrentTimeStamp(watchdog);
        watchdog += 5.f;

        PLT_DeviceDataReference device;
        for (;;) {
            if (NPT_SUCCEEDED(upnp->m_MediaBrowser->FindServer(uuid, device)) && !device.IsNull())
                break;

            // fail right away if device not found and upnp client was already running
            if (client_started)
                goto failure;

            // otherwise check if we've waited long enough without success
            NPT_TimeStamp now;
            NPT_System::GetCurrentTimeStamp(now);
            if (now > watchdog)
                goto failure;

            // sleep a bit and try again
            NPT_System::Sleep(NPT_TimeInterval(1, 0));
        }

        // issue a browse request with object_id
        // if object_id is empty use "0" for root
        object_id = object_id.IsEmpty()?"0":object_id;

        // just a guess as to what types of files we want
        bool video = true;
        bool audio = true;
        bool image = true;
        m_strFileMask.TrimLeft("/");
        if (!m_strFileMask.IsEmpty()) {
            video = m_strFileMask.Find(".wmv") >= 0;
            audio = m_strFileMask.Find(".wma") >= 0;
            image = m_strFileMask.Find(".jpg") >= 0;
        }

        // special case for Windows Media Connect and WMP11 when looking for root
        // We can target which root subfolder we want based on directory mask
        if (object_id == "0" && ((device->GetFriendlyName().Find("Windows Media Connect", 0, true) >= 0) ||
                                 (device->m_ModelName == "Windows Media Player Sharing"))) {

            // look for a specific type to differentiate which folder we want
            if (audio && !video && !image) {
                // music
                object_id = "1";
            } else if (!audio && video && !image) {
                // video
                object_id = "2";
            } else if (!audio && !video && image) {
                // pictures
                object_id = "3";
            }
        }

#ifdef DISABLE_SPECIALCASE
        // same thing but special case for XBMC
        if (object_id == "0" && ((device->m_ModelName.Find("XBMC", 0, true) >= 0) ||
                                 (device->m_ModelName.Find("Xbox Media Center", 0, true) >= 0))) {
            // look for a specific type to differentiate which folder we want
            if (audio && !video && !image) {
                // music
                object_id = "virtualpath://upnpmusic";
            } else if (!audio && video && !image) {
                // video
                object_id = "virtualpath://upnpvideo";
            } else if (!audio && !video && image) {
                // pictures
                object_id = "virtualpath://upnppictures";
            }
        }
#endif

        // if error, return now, the device could have gone away
        // this will make us go back to the sources list
        PLT_MediaObjectListReference list;
        NPT_Result res = upnp->m_MediaBrowser->BrowseSync(device, object_id, list);
        if (NPT_FAILED(res)) goto failure;

        // empty list is ok
        if (list.IsNull()) goto cleanup;

        PLT_MediaObjectList::Iterator entry = list->GetFirstItem();
        while (entry) {
            // disregard items with wrong class/type
            if( (!video && (*entry)->m_ObjectClass.type.CompareN("object.item.videoitem", 21,true) == 0)
             || (!audio && (*entry)->m_ObjectClass.type.CompareN("object.item.audioitem", 21,true) == 0)
             || (!image && (*entry)->m_ObjectClass.type.CompareN("object.item.imageitem", 21,true) == 0) )
            {
                ++entry;
                continue;
            }

            // never show empty containers in media views
            if((*entry)->IsContainer()) {
                if( (audio || video || image)
                 && ((PLT_MediaContainer*)(*entry))->m_ChildrenCount == 0) {
                    ++entry;
                    continue;
                }
            }

            CFileItemPtr pItem(new CFileItem((const char*)(*entry)->m_Title));
            pItem->SetLabelPreformated(true);
            pItem->m_strTitle = (const char*)(*entry)->m_Title;
            pItem->m_bIsFolder = (*entry)->IsContainer();

            // if it's a container, format a string as upnp://uuid/object_id
            if (pItem->m_bIsFolder) {
                CStdString id = (char*) (*entry)->m_ObjectID;
                CUtil::URLEncode(id);
                pItem->m_strPath = (const char*) "upnp://" + uuid + "/" + id.c_str() + "/";
            } else {
                if ((*entry)->m_Resources.GetItemCount()) {
                    PLT_MediaItemResource& resource = (*entry)->m_Resources[0];

                    // look for a resource with "xbmc-get" protocol
                    // if we can't find one, keep the first resource
                    NPT_ContainerFind((*entry)->m_Resources,
                                      CProtocolFinder("xbmc-get"),
                                      resource);

                    CLog::Log(LOGDEBUG, "CUPnPDirectory::GetDirectory - resource protocol info '%s'",
                        (const char*)(resource.m_ProtocolInfo.ToString()));

                    // if it's an item, path is the first url to the item
                    // we hope the server made the first one reachable for us
                    // (it could be a format we dont know how to play however)
                    pItem->m_strPath = (const char*) resource.m_Uri;

                    // set metadata
                    if (resource.m_Size != (NPT_LargeSize)-1) {
                        pItem->m_dwSize  = resource.m_Size;
                    }

                    // set a general content type
                    CStdString type = (const char*)(*entry)->m_ObjectClass.type.Left(21);
                    if (type.Equals("object.item.videoitem"))
                        pItem->SetMimeType("video/octet-stream");
                    else if(type.Equals("object.item.audioitem"))
                        pItem->SetMimeType("audio/octet-stream");
                    else if(type.Equals("object.item.imageitem"))
                        pItem->SetMimeType("image/octet-stream");

                    // look for content type in protocol info
                    if (resource.m_ProtocolInfo.IsValid()) {
                        if (resource.m_ProtocolInfo.GetContentType().Compare("application/octet-stream") != 0) {
                            pItem->SetMimeType((const char*)resource.m_ProtocolInfo.GetContentType());
                        }
                    } else {
                        CLog::Log(LOGERROR, "CUPnPDirectory::GetDirectory - invalid protocol info '%s'",
                            (const char*)(resource.m_ProtocolInfo.ToString()));
                    }

                    // look for date?
                    if((*entry)->m_Description.date.GetLength()) {
                        SYSTEMTIME time = {};
                        sscanf((*entry)->m_Description.date, "%hu-%hu-%huT%hu:%hu:%hu",
                               &time.wYear, &time.wMonth, &time.wDay, &time.wHour, &time.wMinute, &time.wSecond);
                        pItem->m_dateTime = time;
                    }

                    // look for metadata
                    if( (*entry)->m_ObjectClass.type.CompareN("object.item.videoitem", 21,true) == 0 ) {
                        pItem->SetLabelPreformated(false);
                        CUPnP::PopulateTagFromObject(*pItem->GetVideoInfoTag(), *(*entry), &resource);
                    } else if( (*entry)->m_ObjectClass.type.CompareN("object.item.audioitem", 21,true) == 0 ) {
                        pItem->SetLabelPreformated(false);
                        CUPnP::PopulateTagFromObject(*pItem->GetMusicInfoTag(), *(*entry), &resource);
                    } else if( (*entry)->m_ObjectClass.type.CompareN("object.item.imageitem", 21,true) == 0 ) {
                      //CPictureInfoTag* tag = pItem->GetPictureInfoTag();
                    }
                }

                // look for subtitles
                unsigned subs = 0;
                for(unsigned r = 0; r < (*entry)->m_Resources.GetItemCount(); r++)
                {
                    PLT_MediaItemResource& res  = (*entry)->m_Resources[r];
                    PLT_ProtocolInfo&      info = res.m_ProtocolInfo;
                    static const char* allowed[] = { "text/srt"
                                                   , "text/ssa"
                                                   , "text/sub"
                                                   , "text/idx" };
                    for(unsigned type = 0; type < sizeof(allowed)/sizeof(allowed[0]); type++)
                    {
                        if(info.Match(PLT_ProtocolInfo("*", "*", allowed[type], "*")))
                        {
                            CStdString prop;
                            prop.Format("upnp:subtitle:%d", ++subs);
                            pItem->SetProperty(prop, (const char*)res.m_Uri);
                            break;
                        }
                    }
                }
            }

            // if there is a thumbnail available set it here
            if((*entry)->m_ExtraInfo.album_art_uri.GetLength())
                pItem->SetThumbnailImage((const char*) (*entry)->m_ExtraInfo.album_art_uri);
            else if((*entry)->m_Description.icon_uri.GetLength())
                pItem->SetThumbnailImage((const char*) (*entry)->m_Description.icon_uri);

            items.Add(pItem);

            ++entry;
        }
    }

cleanup:
    return true;

failure:
    return false;
}
Example #6
0
/*----------------------------------------------------------------------
|   PLT_MediaObject::FromDidl
+---------------------------------------------------------------------*/
NPT_Result
PLT_MediaObject::FromDidl(NPT_XmlElementNode* entry)
{
    NPT_String str, xml;
    NPT_Array<NPT_XmlElementNode*> children;
    NPT_Result res;

    // check if item is restricted (is default true?)
    if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(entry, "restricted", str))) {
        m_Restricted = PLT_Service::IsTrue(str);
    }

    res = PLT_XmlHelper::GetAttribute(entry, "id", m_ObjectID);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);

    res = PLT_XmlHelper::GetAttribute(entry, "parentID", m_ParentID);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);

    res = PLT_XmlHelper::GetAttribute(entry, "refID", m_ReferenceID);

    res = PLT_XmlHelper::GetChildText(entry, "title", m_Title, didl_namespace_dc);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);

    res = PLT_XmlHelper::GetChildText(entry, "class", m_ObjectClass.type, didl_namespace_upnp);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);

    // read non-required elements
    PLT_XmlHelper::GetChildText(entry, "creator", m_Creator, didl_namespace_dc);
    PLT_XmlHelper::GetChildText(entry, "date", m_Date, didl_namespace_dc);

    PLT_XmlHelper::GetChildren(entry, children, "artist", didl_namespace_upnp);
    m_People.artists.FromDidl(children);

    PLT_XmlHelper::GetChildText(entry, "album", m_Affiliation.album, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "programTitle", m_Recorded.program_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "seriesTitle", m_Recorded.series_title, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "episodeNumber", str, didl_namespace_upnp);
    NPT_UInt32 value;
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_Recorded.episode_number = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "genre", didl_namespace_upnp);
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        if (children[i]->GetText()) {
            m_Affiliation.genres.Add(*children[i]->GetText());
        }
    }

    PLT_XmlHelper::GetChildText(entry, "albumArtURI", m_ExtraInfo.album_art_uri, didl_namespace_upnp);
    PLT_XmlHelper::GetChildText(entry, "longDescription", m_Description.long_description, didl_namespace_upnp);
	PLT_XmlHelper::GetChildText(entry, "toc", m_MiscInfo.toc, didl_namespace_upnp);

    PLT_XmlHelper::GetChildText(entry, "originalTrackNumber", str, didl_namespace_upnp);
    if (NPT_FAILED(str.ToInteger(value))) value = 0;
    m_MiscInfo.original_track_number = value;

    children.Clear();
    PLT_XmlHelper::GetChildren(entry, children, "res");
    for (NPT_Cardinal i=0; i<children.GetItemCount(); i++) {
        PLT_MediaItemResource resource;

        // extract url
        if (children[i]->GetText() == NULL) {
            NPT_LOG_WARNING_1("No resource text found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_Uri = *children[i]->GetText();

            // basic uri validation, ignoring scheme (could be rtsp)
            NPT_HttpUrl url(resource.m_Uri, true);
            if (!url.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource uri: %s", (const char*)resource.m_Uri);
            }
        }

        // extract protocol info
        NPT_String protocol_info;
        res = PLT_XmlHelper::GetAttribute(children[i], "protocolInfo", protocol_info);
        if (NPT_FAILED(res)) {
            NPT_LOG_WARNING_1("No protocol info found in: %s", (const char*)PLT_XmlHelper::Serialize(*children[i]));
        } else {
            resource.m_ProtocolInfo = PLT_ProtocolInfo(protocol_info);
            if (!resource.m_ProtocolInfo.IsValid()) {
                NPT_LOG_WARNING_1("Invalid resource protocol info: %s", (const char*)protocol_info);
            }
        }

        // extract known attributes
        PLT_XmlHelper::GetAttribute(children[i], "protection", resource.m_Protection);
        PLT_XmlHelper::GetAttribute(children[i], "resolution", resource.m_Resolution);

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "size", str))) {
            if (NPT_FAILED(str.ToInteger64(resource.m_Size))) resource.m_Size = (NPT_Size)-1;
        }

        if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(children[i], "duration", str))) {
            if (NPT_FAILED(PLT_Didl::ParseTimeStamp(str, resource.m_Duration))) {
                // if error while converting, ignore and set to -1 to indicate we don't know the duration
                resource.m_Duration = (NPT_UInt32)-1;
                PLT_XmlHelper::RemoveAttribute(children[i], "duration");
            } else {
                // DLNA: reformat duration in case it was not compliant
                str = PLT_Didl::FormatTimeStamp(resource.m_Duration);
                PLT_XmlHelper::SetAttribute(children[i], "duration", str); 
            }
        }    
        m_Resources.Add(resource);
    }

    // re serialize the entry didl as a we might need to pass it to a renderer
    // we may have modified the tree to "fix" issues, so as not to break a renderer
    // (don't write xml prefix as this didl could be part of a larger document)
    res = PLT_XmlHelper::Serialize(*entry, xml, false);
    NPT_CHECK_LABEL_SEVERE(res, cleanup);
    
    m_Didl = didl_header + xml + didl_footer;    
    return NPT_SUCCESS;

cleanup:
    return res;
}