Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

UPNP: Support multiple artwork sizes for recordings, video and music

Additionally:
* Add support for coverart albumArtwork for recordings
* Send preview image for recordings as a resource <res> instead
  • Loading branch information...
commit 378fe051f1d96d4e341391a1a21c6f0adec83f65 1 parent 212d610
@stuartm stuartm authored
View
10 mythtv/libs/libmythupnp/upnpcdsobjects.cpp
@@ -401,7 +401,10 @@ CDSObject *CDSObject::CreateMusicTrack( QString sId, QString sTitle, QString sPa
pObject->AddProperty( new Property( "contributor" , "dc" ));
pObject->AddProperty( new Property( "date" , "dc" ));
- pObject->AddProperty( new Property( "albumArtURI" , "upnp" ));
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true)); // TN
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true)); // SM
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true)); // MED
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true)); // LRG
return( pObject );
}
@@ -477,8 +480,11 @@ CDSObject *CDSObject::CreateVideoItem( QString sId, QString sTitle, QString sPar
pObject->AddProperty( new Property( "album" , "upnp" ));
pObject->AddProperty( new Property( "date" , "dc" ));
- pObject->AddProperty( new Property( "albumArtURI" , "upnp" ));
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true )); // TN
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true )); // SM
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true )); // MED
+ pObject->AddProperty( new Property( "albumArtURI", "upnp", false, "", true )); // LRG
return( pObject );
}
View
49 mythtv/programs/mythbackend/services/content.cpp
@@ -193,7 +193,7 @@ QFileInfo Content::GetImageFile( const QString &sStorageGroup,
if ( nHeight == 0 )
nHeight = (int)rint(nWidth / fAspect);
- QImage img = pImage->scaled( nWidth, nHeight, Qt::IgnoreAspectRatio,
+ QImage img = pImage->scaled( nWidth, nHeight, Qt::KeepAspectRatio,
Qt::SmoothTransformation);
QByteArray fname = sNewFileName.toLatin1();
@@ -364,10 +364,7 @@ QFileInfo Content::GetAlbumArt( int nTrackId, int nWidth, int nHeight )
if (!RemoteFile::Exists(sFullFileName))
return QFileInfo();
- if ((nWidth == 0) && (nHeight == 0))
- return QFileInfo( sFullFileName );
-
- QString sNewFileName = QString( "/tmp/%1.%2x%3.png" )
+ QString sNewFileName = QString( "/tmp/%1.%2x%3.jpg" )
.arg( QFileInfo(sFullFileName).fileName() )
.arg( nWidth )
.arg( nHeight );
@@ -383,7 +380,7 @@ QFileInfo Content::GetAlbumArt( int nTrackId, int nWidth, int nHeight )
// Must generate Albumart Image, Generate Image and save.
// ----------------------------------------------------------------------
- float fAspect = 0.0;
+
QImage img;
if (sFullFileName.startsWith("myth://"))
{
@@ -399,20 +396,42 @@ QFileInfo Content::GetAlbumArt( int nTrackId, int nWidth, int nHeight )
if (img.isNull())
return QFileInfo();
- if (fAspect <= 0)
- fAspect = (float)(img.width()) / img.height();
+ // We don't need to scale if no height and width were specified
+ // but still need to save as jpg if it's in another format
+ if ((nWidth == 0) && (nHeight == 0))
+ {
+ QFileInfo fi(sFullFileName);
+ if (fi.suffix().toLower() == "jpg")
+ return fi;
+ }
+ else if (nWidth > img.width() && nHeight > img.height())
+ {
+ // Requested dimensions are larger than the source image, so instead of
+ // scaling up which will produce horrible results return the fullsize
+ // image and the user can scale further if they want instead
+ // NOTE: If this behaviour is changed, for example making it optional,
+ // then upnp code will need changing to compensate
+ }
+ else
+ {
+ float fAspect = 0.0;
+ if (fAspect <= 0)
+ fAspect = (float)(img.width()) / img.height();
- if ( nWidth == 0 )
- nWidth = (int)rint(nHeight * fAspect);
+ if ( nWidth == 0 || nWidth > img.width() )
+ nWidth = (int)rint(nHeight * fAspect);
- if ( nHeight == 0 )
- nHeight = (int)rint(nWidth / fAspect);
+ if ( nHeight == 0 || nHeight > img.height() )
+ nHeight = (int)rint(nWidth / fAspect);
- img = img.scaled( nWidth, nHeight, Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation);
+ img = img.scaled( nWidth, nHeight, Qt::KeepAspectRatio,
+ Qt::SmoothTransformation);
+ }
QString fname = sNewFileName.toLatin1().constData();
- if (!img.save( fname, "PNG" ))
+ // Use JPG not PNG for compatibility with the most uPnP devices and
+ // faster loading (smaller file to send over network)
+ if (!img.save( fname, "JPG" ))
return QFileInfo();
return QFileInfo( sNewFileName );
View
61 mythtv/programs/mythbackend/upnpcdsmusic.cpp
@@ -355,13 +355,62 @@ void UPnpCDSMusic::AddItem( const UPnpCDSRequest *pRequest,
QString sArtURI = QString( "%1GetAlbumArt?Id=%2").arg( sURIBase )
.arg( nId );
- pItem->SetPropValue( "albumArtURI", sArtURI );
- Property *pProp = pItem->GetProperty("albumArtURI");
- if (pProp)
+ QList<Property*> propList = pItem->GetProperties("albumArtURI");
+ if (propList.size() >= 4)
{
- pProp->AddAttribute("dlna:profileID", "PNG_TN");
- pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
-
+ // Prefer JPEG over PNG here, although PNG is allowed JPEG probably
+ // has wider device support and crucially the filesizes are smaller
+ // which speeds up loading times over the network
+
+ // We MUST include the thumbnail size, but since some clients may use the
+ // first image they see and the thumbnail is tiny, instead return the
+ // medium first. The large could be very large, which is no good if the
+ // client is pulling images for an entire list at once!
+
+ // Medium
+ Property *pProp = propList.at(0);
+ if (pProp)
+ {
+ // Must be no more than 1024x768
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=1024&amp;Height=768");
+ pProp->AddAttribute("dlna:profileID", "JPG_MED");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Thumbnail
+ pProp = propList.at(1);
+ if (pProp)
+ {
+ // At least one albumArtURI must be a ThumbNail (TN) no larger
+ // than 160x160, and it must also be a jpeg
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=160&amp;Height=160");
+ pProp->AddAttribute("dlna:profileID", "JPG_TN");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Small
+ pProp = propList.at(2);
+ if (pProp)
+ {
+ // Must be no more than 640x480
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=640&amp;Height=480");
+ pProp->AddAttribute("dlna:profileID", "JPG_SM");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Large
+ pProp = propList.at(3);
+ if (pProp)
+ {
+ // Must be no more than 4096x4096 - for our purposes, just return
+ // a fullsize image
+ pProp->m_sValue = sArtURI;
+ pProp->AddAttribute("dlna:profileID", "JPG_LRG");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
}
pResults->Add( pItem );
View
83 mythtv/programs/mythbackend/upnpcdstv.cpp
@@ -147,7 +147,7 @@ QString UPnpCDSTv::GetItemListSQL( QString /* sColumn */ )
"subtitle, description, category, " \
"hostname, recgroup, filesize, " \
"basename, progstart, progend, " \
- "storagegroup " \
+ "storagegroup, inetref " \
"FROM recorded ";
}
@@ -302,6 +302,8 @@ void UPnpCDSTv::AddItem( const UPnpCDSRequest *pRequest,
MythDate::as_utc(query.value(12).toDateTime());
QString sStorageGrp = query.value(13).toString();
+ QString sInetRef = query.value(14).toString();
+
// ----------------------------------------------------------------------
// Cache Host ip Address & Port
// ----------------------------------------------------------------------
@@ -457,22 +459,85 @@ void UPnpCDSTv::AddItem( const UPnpCDSRequest *pRequest,
*/
// ----------------------------------------------------------------------
- // Add Preview URI as albumArt
+ // Add Preview URI as <res>
+ // MUST be _TN and 160px
// ----------------------------------------------------------------------
sURI = QString( "%1GetPreviewImage%2%3").arg( sURIBase )
.arg( sURIParams )
- .arg( "&amp;Width=160" );
+ .arg( "&amp;Width=160" );
+
+ // TODO: Must be JPG for minimal compliance
+ sProtocol = QString( "http-get:*:image/png:DLNA.ORG_PN=PNG_TN");
+ pRes = pItem->AddResource( sProtocol, sURI );
+
+ // ----------------------------------------------------------------------
+ // Add Artwork URI as albumArt
+ // ----------------------------------------------------------------------
+
+ sURI = QString( "%1GetRecordingArtwork?Type=coverart&amp;Inetref=%3")
+ .arg( sURIBase )
+ .arg( sInetRef );
- pItem->SetPropValue( "albumArtURI", sURI );
- Property *pProp = pItem->GetProperty("albumArtURI");
- if (pProp)
+ QList<Property*> propList = pItem->GetProperties("albumArtURI");
+ if (propList.size() >= 4)
{
- pProp->AddAttribute("dlna:profileID", "PNG_TN");
- pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
-
+ // Prefer JPEG over PNG here, although PNG is allowed JPEG probably
+ // has wider device support and crucially the filesizes are smaller
+ // which speeds up loading times over the network
+
+ // We MUST include the thumbnail size, but since some clients may use the
+ // first image they see and the thumbnail is tiny, instead return the
+ // medium first. The large could be very large, which is no good if the
+ // client is pulling images for an entire list at once!
+
+ // Medium
+ Property *pProp = propList.at(0);
+ if (pProp)
+ {
+ // Must be no more than 1024x768
+ pProp->m_sValue = sURI;
+ pProp->m_sValue.append("&amp;Width=1024&amp;Height=768");
+ pProp->AddAttribute("dlna:profileID", "JPG_MED");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Thumbnail
+ pProp = propList.at(1);
+ if (pProp)
+ {
+ // At least one albumArtURI must be a ThumbNail (TN) no larger
+ // than 160x160, and it must also be a jpeg
+ pProp->m_sValue = sURI;
+ pProp->m_sValue.append("&amp;Width=160&amp;Height=160");
+ pProp->AddAttribute("dlna:profileID", "JPG_TN");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Medium
+ pProp = propList.at(2);
+ if (pProp)
+ {
+ // Must be no more than 1024x768
+ pProp->m_sValue = sURI;
+ pProp->m_sValue.append("&amp;Width=1024&amp;Height=768");
+ pProp->AddAttribute("dlna:profileID", "JPG_MED");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Large
+ pProp = propList.at(3);
+ if (pProp)
+ {
+ // Must be no more than 4096x4096 - for our purposes, just return
+ // a fullsize image
+ pProp->m_sValue = sURI;
+ pProp->AddAttribute("dlna:profileID", "JPG_LRG");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
}
+
}
// vim:ts=4:sw=4:ai:et:si:sts=4
View
68 mythtv/programs/mythbackend/upnpcdsvideo.cpp
@@ -312,10 +312,6 @@ void UPnpCDSVideo::AddItem( const UPnpCDSRequest *pRequest,
QString sParentID = "Videos/0";
- QString sAlbumArtURI= QString( "%1GetVideoArtwork%2")
- .arg( sURIBase )
- .arg( sURIParams );
-
CDSObject *pItem = CDSObject::CreateVideoItem( sId, sName, sParentID );
pItem->m_bRestricted = false;
@@ -339,8 +335,68 @@ void UPnpCDSVideo::AddItem( const UPnpCDSRequest *pRequest,
//pItem->SetPropValue( "relation" , );
//pItem->SetPropValue( "region" , );
- if ((!sCoverArt.isEmpty()) && (sCoverArt != "No Cover"))
- pItem->SetPropValue( "albumArtURI" , sAlbumArtURI);
+ QString sArtURI= QString( "%1GetVideoArtwork%2")
+ .arg( sURIBase )
+ .arg( sURIParams );
+
+ QList<Property*> propList = pItem->GetProperties("albumArtURI");
+ if ((!sCoverArt.isEmpty()) && (sCoverArt != "No Cover")
+ && propList.size() >= 4)
+ {
+ // Prefer JPEG over PNG here, although PNG is allowed JPEG probably
+ // has wider device support and crucially the filesizes are smaller
+ // which speeds up loading times over the network
+
+ // We MUST include the thumbnail size, but since some clients may use the
+ // first image they see and the thumbnail is tiny, instead return the
+ // medium first. The large could be very large, which is no good if the
+ // client is pulling images for an entire list at once!
+
+ // Medium
+ Property *pProp = propList.at(0);
+ if (pProp)
+ {
+ // Must be no more than 1024x768
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=1024&amp;Height=768");
+ pProp->AddAttribute("dlna:profileID", "JPG_MED");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Thumbnail
+ pProp = propList.at(1);
+ if (pProp)
+ {
+ // At least one albumArtURI must be a ThumbNail (TN) no larger
+ // than 160x160, and it must also be a jpeg
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=160&amp;Height=160");
+ pProp->AddAttribute("dlna:profileID", "JPG_TN");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Medium
+ pProp = propList.at(2);
+ if (pProp)
+ {
+ // Must be no more than 1024x768
+ pProp->m_sValue = sArtURI;
+ pProp->m_sValue.append("&amp;Width=1024&amp;Height=768");
+ pProp->AddAttribute("dlna:profileID", "JPG_MED");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+
+ // Large
+ pProp = propList.at(3);
+ if (pProp)
+ {
+ // Must be no more than 4096x4096 - for our purposes, just return
+ // a fullsize image
+ pProp->m_sValue = sArtURI;
+ pProp->AddAttribute("dlna:profileID", "JPG_LRG");
+ pProp->AddAttribute("xmlns:dlna", "urn:schemas-dlna-org:metadata-1-0");
+ }
+ }
if ( bAddRef )
{
Please sign in to comment.
Something went wrong with that request. Please try again.