MythTV  master
upnphelpers.cpp
Go to the documentation of this file.
1 
2 #include "upnphelpers.h"
3 
6 
7 namespace UPnPDateTime
8 {
9 
10 QString DurationFormat(std::chrono::milliseconds msec)
11 {
12  // Appendix D. Date&Time Syntax - UPnP ContentDirectory Service 2008, 2013
13  // duration ::= 'P' [n 'D'] time
14  // time ::= HH ':' MM ':' SS
15  //
16  // e.g.
17  //"<upnp:scheduledDuration>P1D02:30:23</upnp:scheduledDuration>"
18  // 1 day 2 Hours 30 Minutes 23 Seconds
19 
20  QString durationStr = "P%1%2";
21  QString dayStr;
22  if ( msec > 24h )
23  {
24  dayStr = QString("D%1").arg((msec % 24h).count());
25  }
26  QString timeStr = UPnPDateTime::TimeFormat(msec);
27 
28  return durationStr.arg(dayStr, timeStr);
29 }
30 
31 QString TimeFormat(const QTime time)
32 {
33  QString timeStr = time.toString("HH:mm:ss");
34  return timeStr;
35 }
36 
37  QString TimeFormat(std::chrono::milliseconds msec)
38 {
39  QTime time = QTime::fromMSecsSinceStartOfDay(msec.count());
40  return time.toString("HH:mm:ss");
41 }
42 
43 QString DateTimeFormat(const QDateTime &dateTime)
44 {
45  QString dateTimeStr = dateTime.toString(Qt::ISODate);
46  return dateTimeStr;
47 }
48 
49 QString NamedDayFormat(const QDateTime &dateTime)
50 {
51  return UPnPDateTime::NamedDayFormat(dateTime.date());
52 }
53 
54 QString NamedDayFormat(const QDate date)
55 {
56  // Note QDate::toString() and QDate::shortDayName() return localized strings
57  // which are of no use to us.
58  //
59  // Valid values per the spec are MON, TUE, WED, THU etc
60  switch (date.dayOfWeek())
61  {
62  case 1:
63  return "MON";
64  case 2:
65  return "TUE";
66  case 3:
67  return "WED";
68  case 4:
69  return "THU";
70  case 5:
71  return "FRI";
72  case 6:
73  return "SAT";
74  case 7:
75  return "SUN";
76  default:
77  return "";
78  }
79 }
80 
81 QString resDurationFormat(std::chrono::milliseconds msec)
82 {
83  // Appendix B.2 Resource Encoding Characteristics Properties
84  // B.2.1.4 res@duration
85 
86  // H[H]:MM:SS[.FS]
87  // H = Hours (1-2 digits),
88  // M = Minutes (2 digits, 0 prefix)
89  // S = Seconds (2 digits, 0 prefix)
90  // FS = Fractional Seconds (milliseconds)
91  QTime time = QTime::fromMSecsSinceStartOfDay(msec.count());
92  return time.toString("H:mm:ss.zzz");
93 }
94 
95 };
96 
97 namespace DLNA
98 {
99 
100 QString DLNAProfileName(const QString &mimeType, const QSize resolution,
101  const double /*videoFrameRate*/, const QString &container,
102  const QString &vidCodec, const QString &audioCodec)
103 {
104  QString sProfileName;
105  bool isHD = (resolution.height() >= 720) || (resolution.width() > 720);
106 
107  //QString sBroadcastStandard = "";
108  // HACK This is just temporary until we start storing more video
109  // information in the database for each file and can determine this
110  // stuff 'properly'
111  QString sCountryCode = gCoreContext->GetLocale()->GetCountryCode();
112  bool isNorthAmerica = (sCountryCode == "us" || sCountryCode == "ca" ||
113  sCountryCode == "mx"); // North America (NTSC/ATSC)
114 
115  if (container == "MPEG2-PS")
116  {
117  if (isHD && audioCodec == "DTS")
118  sProfileName = "MPEG_PS_HD_DTS";
119  else if (isHD)
120  {
121  // Fallthough, no DLNA profiles
122  }
123  else if (audioCodec == "DTS")
124  sProfileName = "MPEG_PS_SD_DTS";
125  else
126  {
127  if (isNorthAmerica)
128  sProfileName = "MPEG_PS_NTSC";
129  else
130  sProfileName = "MPEG_PS_PAL";
131  }
132  }
133  else if (container == "MPEG2-TS")
134  {
135  if (isNorthAmerica)
136  {
137  if (vidCodec == "H264")
138  sProfileName = "AVC_TS_NA_ISO";
139  else if (isHD) // && videoCodec == "MPEG2VIDEO"
140  sProfileName = "MPEG_TS_HD_NA_ISO";
141  else // if (videoCodec == "MPEG2VIDEO")
142  sProfileName = "MPEG_TS_SD_NA_ISO";
143  }
144  else // Europe standard (DVB)
145  {
146  if (vidCodec == "H264" || isHD) // All HD is AVC with DVB
147  sProfileName = "AVC_TS_EU_ISO";
148  else // if (videoCodec == "MPEG2VIDEO")
149  sProfileName = "MPEG_TS_SD_EU_ISO";
150  }
151  }
152  else if (mimeType == "video/x-matroska" || container == "MATROSKA")
153  {
154  // TODO: We need to know the video and audio codecs before we can serve
155  // up the correct profile.
156  //
157  // sAdditionalInfoList << "DLNA.ORG_PN=AVC_MKV_SOMETHING";
158  if (vidCodec == "H264")
159  {
160  // TODO We need to know the H264 profile used, for now we go with High Profile
161  // as this covers all - devices supporting High Profile also support
162  // Medium Profile
163  if (audioCodec == "AAC") // No HEAAC detection atm, it's a sub-profile of AAC
164  {
165  sProfileName = "AVC_MKV_HP_HD_AAC_MULT5";
166  }
167  else if (audioCodec == "AC3")
168  {
169  sProfileName = "AVC_MKV_HP_HD_AC3";
170  }
171  else if (audioCodec == "E-AC3") // Up to 35Mbps
172  {
173  sProfileName = "AVC_MKV_HP_HD_EAC3";
174  }
175  else if (audioCodec == "MP3")
176  {
177  sProfileName = "AVC_MKV_HP_HD_MPEG1_L3";
178  }
179  else if (audioCodec == "DTS") // Up to 55Mbps // No DTSE/DTSL detection atm, sub-profiles of DTS
180  {
181  sProfileName = "AVC_MKV_HP_HD_DTS";
182  }
183  else if (audioCodec == "MLP") // Up to 45Mbps
184  {
185  sProfileName = "AVC_MKV_HP_HD_MLP";
186  }
187  }
188  }
189  else if (mimeType == "audio/mpeg")
190  {
191  sProfileName = "MP3X";
192  }
193  else if (mimeType == "audio/x-ms-wma")
194  {
195  sProfileName = "WMAFULL";
196  }
197  else if (mimeType == "audio/vnd.dolby.dd-raw")
198  {
199  sProfileName = "AC3";
200  }
201  else if (mimeType == "audio/mp4")
202  {
203  sProfileName = "AAC_ISO_320";
204  }
205  else if (mimeType == "image/jpeg")
206  {
207  if (resolution.width() <= 160 && resolution.height() <= 160)
208  sProfileName = "JPEG_TN";
209  else if (resolution.width() <= 640 && resolution.height() <= 480)
210  sProfileName = "JPEG_SM";
211  else if (resolution.width() <= 1024 && resolution.height() <= 768)
212  sProfileName = "JPEG_MED";
213  else if (resolution.width() <= 4096 && resolution.height() <= 4096)
214  sProfileName = "JPEG_LRG";
215  }
216  else if (mimeType == "image/png")
217  {
218  if (resolution.width() <= 160 && resolution.height() <= 160)
219  sProfileName = "PNG_TN";
220  else if (resolution.width() <= 4096 && resolution.height() <= 4096)
221  sProfileName = "PNG_LRG";
222  }
223  else if (mimeType == "image/gif")
224  {
225  if (resolution.width() <= 1600 && resolution.height() <= 1200)
226  sProfileName = "GIF_LRG";
227  }
228  else
229  {
230  // Not a DLNA supported profile?
231  }
232 
233  return sProfileName;
234 }
235 
237  const QString &mimeType, const QSize resolution,
238  double videoFrameRate, const QString &container,
239  const QString &videoCodec, const QString &audioCodec,
240  bool isTranscoded)
241 {
242 
243  QStringList sAdditionalInfoList;
244  //
245  // 4th_field = pn-param [op-param] [ps-param] [ci-param] [flags-param] [ *(other-param)]
246  //
247 
248  //
249  // PN-Param (Profile Name)
250  //
251  QString sProfileName = DLNAProfileName(mimeType, resolution, videoFrameRate,
252  container, videoCodec, audioCodec);
253  if (!sProfileName.isEmpty())
254  sAdditionalInfoList << QString("DLNA.ORG_PN=%1").arg(sProfileName);
255 
256  //
257  // OP-Param (Operation Parameters)
258  //
259  sAdditionalInfoList << DLNA::OpParamString(protocol);
260 
261  //
262  // PS-Param (Play Speed)
263  //
264 
265  // Not presently used by MythTV
266 
267  //
268  // CI-Param - Optional, may be omitted if value is zero (0)
269  //
270  if (isTranscoded)
271  sAdditionalInfoList << DLNA::ConversionIndicatorString(isTranscoded);
272 
273  //
274  // Flags-Param
275  //
276  if (mimeType.startsWith("audio") || mimeType.startsWith("video"))
277  {
278  sAdditionalInfoList << DLNA::FlagsString(DLNA::ktm_s |
279  DLNA::ktm_b |
281  }
282  else
283  {
284  sAdditionalInfoList << DLNA::FlagsString(DLNA::ktm_i |
285  DLNA::ktm_b |
287  }
288 
289 
290  //
291  // Build the complete string
292  //
293  QString sAdditionalInfo = "*";
294  // If we have DLNA additional info and we have the mandatory PN param
295  // then add it to the string. If the PN is missing then we must ignore the
296  // rest
297  // 7.4.1.3.13.8 - "b) The pn-param (DLNA.ORG_PN) is the only required
298  // parameter for DLNA media format profiles."
299  //
300  // 7.4.1.3.17.1 - 4th_field = pn-param [op-param] [ps-param] [ci-param] [flags-param] [ *(other-param)]
301  // - "b) This syntax prohibits the use of the "*" value for
302  // content that conforms to a DLNA media format profile.
303  // Content that does not conform to a DLNA media format
304  // profile can use the "*" value in the 4th field.
305 
306  if (!sAdditionalInfoList.isEmpty() &&
307  sAdditionalInfoList.first().startsWith("DLNA.ORG_PN"))
308  sAdditionalInfo = sAdditionalInfoList.join(";");
309 
310  return sAdditionalInfo;
311 }
312 
313 
314 // NOTE The order of the DLNA args is mandatory - 7.4.1.3.17 MM protocolInfo values: 4th field
316  const QString &mimeType, const QSize resolution,
317  double videoFrameRate, const QString &container,
318  const QString &videoCodec, const QString &audioCodec,
319  bool isTranscoded)
320 {
321  QStringList protocolInfoFields;
322 
323  //
324  // 1st Field = protocol
325  //
326 
327  if (protocol == UPNPProtocol::kHTTP)
328  protocolInfoFields << "http-get";
329  else if (protocol == UPNPProtocol::kRTP)
330  protocolInfoFields << "rtsp-rtp-udp";
331 
332  //
333  // 2nd Field =
334  //
335 
336  // Not applicable to us, return wildcard
337  protocolInfoFields << "*";
338 
339  //
340  // 3rd Field = mime type
341  //
342  protocolInfoFields << mimeType;
343 
344  //
345  // 4th Field = Additional Implementation Information (Used by DLNA)
346  //
347  protocolInfoFields << DLNAFourthField(protocol, mimeType, resolution,
348  videoFrameRate, container,
349  videoCodec, audioCodec,
350  isTranscoded);
351 
352  if (protocolInfoFields.size() != 4)
353  LOG(VB_GENERAL, LOG_CRIT, "DLNA::ProtocolInfoString() : Invalid number of fields in string");
354 
355  QString str = protocolInfoFields.join(":");
356 
357  if (str.length() > 256)
358  {
359  LOG(VB_GENERAL, LOG_WARNING, "DLNA::ProtocolInfoString() : ProtocolInfo string exceeds "
360  "256 byte limit for compatibility with older UPnP devices. "
361  "Consider omitting optional DLNA information such as ci-param");
362  }
363  return str;
364 }
365 
366 
367 QString FlagsString(uint32_t flags)
368 {
369  QString str;
370 
371  if (flags == 0)
372  return str;
373 
374  // DLNA::kv1_5_flag must be set
375  if ((flags & DLNA::ktm_s) && (flags & DLNA::ktm_i))
376  {
377  LOG(VB_GENERAL, LOG_ERR, "Programmer Error: 'Streaming' and 'Interactive' mode flags are mutally exclusive");
378  flags &= ~(DLNA::ktm_s | DLNA::ktm_i);
379  }
380 
381  // DLNA::kv1_5_flag must be set otherwise other flags are ignored
382  if (flags & ~DLNA::kv1_5_flag)
383  flags |= DLNA::kv1_5_flag;
384 
385  return QString("DLNA.ORG_FLAGS=%1") // 8 HEX Digits, following by 24 zeros
386  .arg(flags,8,10,QChar('0')) + "000000000000000000000000";
387 }
388 
390 {
391  QString str = "DLNA.ORG_OP=%1%2";
392 
393  if (protocol == UPNPProtocol::kHTTP)
394  {
395  // Section 7.4.1.3.20
396  str = str.arg("0", // A-val - DLNA Time Seek header support (not currently supported)
397  "1"); // B-val - HTTP Range support
398  }
399  else if (protocol == UPNPProtocol::kRTP)
400  {
401  // Section 7.4.1.3.21
402  str = str.arg("0", // A-val - Range Header Support
403  "0"); // B-val - Must always zero
404  }
405 
406  return str;
407 }
408 
409 QString ConversionIndicatorString(bool wasConverted)
410 {
411  QString str = "DLNA.ORG_CI=%1";
412 
413  return str.arg(wasConverted ? "1" : "0");
414 }
415 
416 };
DLNA::ktm_s
@ ktm_s
Definition: upnphelpers.h:254
DLNA::ktm_i
@ ktm_i
Definition: upnphelpers.h:255
UPNPProtocol::kHTTP
@ kHTTP
Definition: upnphelpers.h:133
MythCoreContext::GetLocale
MythLocale * GetLocale(void) const
Definition: mythcorecontext.cpp:1758
UPnPDateTime::DateTimeFormat
QString DateTimeFormat(const QDateTime &dateTime)
Date-Time Format.
Definition: upnphelpers.cpp:43
UPNPProtocol::kRTP
@ kRTP
Definition: upnphelpers.h:134
DLNA::OpParamString
QString OpParamString(UPNPProtocol::TransferProtocol protocol)
Create a properly formatted Operations Parameter (op-param) string for the given transport protocol b...
Definition: upnphelpers.cpp:389
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
DLNA::ProtocolInfoString
QString ProtocolInfoString(UPNPProtocol::TransferProtocol protocol, const QString &mimeType, const QSize resolution, double videoFrameRate, const QString &container, const QString &videoCodec, const QString &audioCodec, bool isTranscoded)
Create a properly formatted string for the 4th field of res@protocolInfo.
Definition: upnphelpers.cpp:315
mythlogging.h
MythLocale::GetCountryCode
QString GetCountryCode() const
Definition: mythlocale.cpp:57
DLNA::kv1_5_flag
@ kv1_5_flag
Definition: upnphelpers.h:262
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
upnphelpers.h
DLNA::DLNAFourthField
QString DLNAFourthField(UPNPProtocol::TransferProtocol protocol, const QString &mimeType, const QSize resolution, double videoFrameRate, const QString &container, const QString &videoCodec, const QString &audioCodec, bool isTranscoded)
Create a properly formatted string for the 4th field of res@protocolInfo.
Definition: upnphelpers.cpp:236
DLNA::ktm_b
@ ktm_b
Definition: upnphelpers.h:256
UPnPDateTime::DurationFormat
QString DurationFormat(std::chrono::milliseconds msec)
Duration Format.
Definition: upnphelpers.cpp:10
UPNPProtocol::TransferProtocol
TransferProtocol
Definition: upnphelpers.h:131
mythcorecontext.h
DLNA::DLNAProfileName
QString DLNAProfileName(const QString &mimeType, const QSize resolution, const double, const QString &container, const QString &vidCodec, const QString &audioCodec)
Try to determine a valid DLNA profile name for the file based on the supplied metadata.
Definition: upnphelpers.cpp:100
DLNA::FlagsString
QString FlagsString(uint32_t flags)
Convert an integer composed of DNLA_Flags to a properly formatted string for use in XML.
Definition: upnphelpers.cpp:367
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:17
UPnPDateTime::TimeFormat
QString TimeFormat(const QTime time)
Time Format.
Definition: upnphelpers.cpp:31
UPnPDateTime
Helpers for formatting dates and times to UPnP, DLNA and Dublin Core specifications.
Definition: upnphelpers.cpp:7
UPnPDateTime::NamedDayFormat
QString NamedDayFormat(const QDateTime &dateTime)
Named-Day Format.
Definition: upnphelpers.cpp:49
DLNA::ConversionIndicatorString
QString ConversionIndicatorString(bool wasConverted)
Create a properly formatted Conversion Indicator (ci-param) String.
Definition: upnphelpers.cpp:409
UPnPDateTime::resDurationFormat
QString resDurationFormat(std::chrono::milliseconds msec)
res@duration Format B.2.1.4 res@duration - UPnP ContentDirectory Service 2008, 2013
Definition: upnphelpers.cpp:81
DLNA
Helpers for building DLNA flag, strings and profiles.
Definition: upnphelpers.cpp:97