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