00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #include "cds_resource_manager.h"
00037 #include "dictionary.h"
00038 #include "object_dictionary.h"
00039 #include "server.h"
00040 #include "common.h"
00041 #include "tools.h"
00042 #include "metadata_handler.h"
00043
00044 using namespace zmm;
00045 using namespace mxml;
00046
00047 CdsResourceManager::CdsResourceManager() : Object()
00048 {
00049 }
00050
00051 String CdsResourceManager::renderExtension(String contentType, String location)
00052 {
00053 String ext = _(_URL_PARAM_SEPARATOR) + URL_FILE_EXTENSION +
00054 _URL_PARAM_SEPARATOR + "file";
00055
00056 if (string_ok(contentType) && (contentType != CONTENT_TYPE_PLAYLIST))
00057 {
00058 ext = ext + _(".") + contentType;
00059 return ext;
00060 }
00061
00062 if (string_ok(location))
00063 {
00064 int dot = location.rindex('.');
00065 if (dot > -1)
00066 {
00067 String extension = location.substring(dot);
00068
00069 if (string_ok(extension) &&
00070 (extension.index(URL_PARAM_SEPARATOR) == -1) &&
00071 (extension.index(URL_PARAM_SEPARATOR) == -1))
00072 {
00073 ext = ext + extension;
00074 return ext;
00075 }
00076 }
00077 }
00078 return nil;
00079 }
00080
00081 void CdsResourceManager::addResources(Ref<CdsItem> item, Ref<Element> element)
00082 {
00083 Ref<UrlBase> urlBase = addResources_getUrlBase(item);
00084 Ref<ConfigManager> config = ConfigManager::getInstance();
00085 bool skipURL = ((IS_CDS_ITEM_INTERNAL_URL(item->getObjectType()) ||
00086 IS_CDS_ITEM_EXTERNAL_URL(item->getObjectType())) &&
00087 (!item->getFlag(OBJECT_FLAG_PROXY_URL)));
00088
00089 bool isExtThumbnail = false;
00090 Ref<Dictionary> mappings = config->getDictionaryOption(
00091 CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
00092
00093 #if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER)
00094 if (config->getBoolOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_ENABLED) &&
00095 (item->getMimeType().startsWith(_("video")) ||
00096 item->getFlag(OBJECT_FLAG_OGG_THEORA)))
00097 {
00098 String videoresolution = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_RESOLUTION));
00099 int x;
00100 int y;
00101
00102 if (string_ok(videoresolution) &&
00103 check_resolution(videoresolution, &x, &y))
00104 {
00105 String thumb_mimetype = mappings->get(_(CONTENT_TYPE_JPG));
00106 if (!string_ok(thumb_mimetype))
00107 thumb_mimetype = _("image/jpeg");
00108
00109 Ref<CdsResource> ffres(new CdsResource(CH_FFTH));
00110 ffres->addParameter(_(RESOURCE_HANDLER), String::from(CH_FFTH));
00111 ffres->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
00112 renderProtocolInfo(thumb_mimetype));
00113 ffres->addOption(_(RESOURCE_CONTENT_TYPE), _(THUMBNAIL));
00114
00115 y = config->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_THUMBSIZE) * y / x;
00116 x = config->getIntOption(CFG_SERVER_EXTOPTS_FFMPEGTHUMBNAILER_THUMBSIZE);
00117 String resolution = String::from(x) + "x" + String::from(y);
00118 ffres->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION),
00119 resolution);
00120 item->addResource(ffres);
00121 log_debug("Adding resource for video thumbnail\n");
00122 }
00123 }
00124 #endif // FFMPEGTHUMBNAILER
00125
00126 #ifdef EXTERNAL_TRANSCODING
00127
00128
00129 int realCount = 0;
00130 bool hide_original_resource = false;
00131 int original_resource = 0;
00132
00133 Ref<UrlBase> urlBase_tr;
00134
00135
00136
00137
00138
00139
00140
00141 Ref<TranscodingProfileList> tlist = config->getTranscodingProfileListOption(
00142 CFG_TRANSCODING_PROFILE_LIST);
00143 Ref<ObjectDictionary<TranscodingProfile> > tp_mt = tlist->get(item->getMimeType());
00144 if (tp_mt != nil)
00145 {
00146 Ref<Array<ObjectDictionaryElement<TranscodingProfile> > > profiles = tp_mt->getElements();
00147 for (int p = 0; p < profiles->size(); p++)
00148 {
00149 Ref<TranscodingProfile> tp = profiles->get(p)->getValue();
00150
00151 if (tp == nil)
00152 throw _Exception(_("Invalid profile encountered!"));
00153
00154 String ct = mappings->get(item->getMimeType());
00155 if (ct == CONTENT_TYPE_OGG)
00156 {
00157 if (((item->getFlag(OBJECT_FLAG_OGG_THEORA)) &&
00158 (!tp->isTheora())) ||
00159 (!item->getFlag(OBJECT_FLAG_OGG_THEORA) &&
00160 (tp->isTheora())))
00161 {
00162 continue;
00163 }
00164 }
00165
00166 else if (ct == CONTENT_TYPE_AVI)
00167 {
00168 avi_fourcc_listmode_t fcc_mode = tp->getAVIFourCCListMode();
00169
00170 Ref<Array<StringBase> > fcc_list = tp->getAVIFourCCList();
00171
00172
00173 if (fcc_mode != FCC_None)
00174 {
00175 String current_fcc = item->getResource(0)->getOption(_(RESOURCE_OPTION_FOURCC));
00176
00177
00178 if (!string_ok(current_fcc))
00179 {
00180
00181
00182
00183 if (fcc_mode == FCC_Process)
00184 continue;
00185 }
00186
00187
00188 else
00189 {
00190 bool fcc_match = false;
00191 for (int f = 0; f < fcc_list->size(); f++)
00192 {
00193 if (current_fcc == fcc_list->get(f)->data)
00194 fcc_match = true;
00195 }
00196
00197 if (!fcc_match && (fcc_mode == FCC_Process))
00198 continue;
00199
00200 if (fcc_match && (fcc_mode == FCC_Ignore))
00201 continue;
00202 }
00203 }
00204 }
00205
00206 if (!item->getFlag(OBJECT_FLAG_DVD_IMAGE) && (tp->onlyDVD()))
00207 continue;
00208
00209 Ref<CdsResource> t_res(new CdsResource(CH_TRANSCODE));
00210 t_res->addParameter(_(URL_PARAM_TRANSCODE_PROFILE_NAME), tp->getName());
00211
00212
00213 t_res->addOption(_(CONTENT_TYPE_OGG),
00214 item->getResource(0)->getOption(_(CONTENT_TYPE_OGG)));
00215 t_res->addParameter(_(URL_PARAM_TRANSCODE), _(URL_VALUE_TRANSCODE));
00216
00217 String targetMimeType = tp->getTargetMimeType();
00218
00219 if (!tp->isThumbnail())
00220 {
00221
00222
00223 String duration = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_DURATION));
00224 if (string_ok(duration))
00225 t_res->addAttribute(MetadataHandler::getResAttrName(R_DURATION),
00226 duration);
00227
00228 int freq = tp->getSampleFreq();
00229 if (freq == SOURCE)
00230 {
00231 String frequency = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY));
00232 if (string_ok(frequency))
00233 {
00234 t_res->addAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY), frequency);
00235 targetMimeType = targetMimeType + _(";rate=") +
00236 frequency;
00237 }
00238 }
00239 else if (freq != OFF)
00240 {
00241 t_res->addAttribute(MetadataHandler::getResAttrName(R_SAMPLEFREQUENCY), String::from(freq));
00242 targetMimeType = targetMimeType + _(";rate=") +
00243 String::from(freq);
00244 }
00245
00246 int chan = tp->getNumChannels();
00247 if (chan == SOURCE)
00248 {
00249 String nchannels = item->getResource(0)->getAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS));
00250 if (string_ok(nchannels))
00251 {
00252 t_res->addAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS), nchannels);
00253 targetMimeType = targetMimeType + _(";channels=") +
00254 nchannels;
00255 }
00256 }
00257 else if (chan != OFF)
00258 {
00259 t_res->addAttribute(MetadataHandler::getResAttrName(R_NRAUDIOCHANNELS), String::from(chan));
00260 targetMimeType = targetMimeType + _(";channels=") +
00261 String::from(chan);
00262 }
00263 }
00264
00265 t_res->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
00266 renderProtocolInfo(targetMimeType));
00267
00268 if (tp->isThumbnail())
00269 t_res->addOption(_(RESOURCE_CONTENT_TYPE), _(EXIF_THUMBNAIL));
00270
00271 t_res->mergeAttributes(tp->getAttributes());
00272
00273 if (tp->hideOriginalResource())
00274 hide_original_resource = true;
00275
00276 if (tp->firstResource())
00277 {
00278 item->insertResource(0, t_res);
00279 original_resource++;
00280 }
00281 else
00282 item->addResource(t_res);
00283 }
00284
00285 if (skipURL)
00286 urlBase_tr = addResources_getUrlBase(item, true);
00287 }
00288
00289 #endif // EXTERNAL_TRANSCODING
00290
00291 int resCount = item->getResourceCount();
00292 for (int i = 0; i < resCount; i++)
00293 {
00295
00296
00297
00298 Ref<Dictionary> res_attrs = item->getResource(i)->getAttributes();
00299 Ref<Dictionary> res_params = item->getResource(i)->getParameters();
00300 String protocolInfo = res_attrs->get(MetadataHandler::getResAttrName(R_PROTOCOLINFO));
00301 String mimeType = getMTFromProtocolInfo(protocolInfo);
00302 assert(string_ok(mimeType));
00303 String contentType = mappings->get(mimeType);
00304 String url;
00305
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 #ifdef EXTERNAL_TRANSCODING
00320
00321 bool transcoded = (res_params->get(_(URL_PARAM_TRANSCODE)) == URL_VALUE_TRANSCODE);
00322 if (!transcoded)
00323 {
00324 if (urlBase->addResID)
00325 {
00326 url = urlBase->urlBase + realCount;
00327 }
00328 else
00329 url = urlBase->urlBase;
00330
00331 realCount++;
00332 }
00333 else
00334 {
00335 if (!skipURL)
00336 url = urlBase->urlBase + _(URL_VALUE_TRANSCODE_NO_RES_ID);
00337 else
00338 {
00339 assert(urlBase_tr != nil);
00340 url = urlBase_tr->urlBase + _(URL_VALUE_TRANSCODE_NO_RES_ID);
00341 }
00342 }
00343 #else
00344 if (urlBase->addResID)
00345 url = urlBase->urlBase + i;
00346 else
00347 url = urlBase->urlBase;
00348 #endif
00349 if ((res_params != nil) && (res_params->size() > 0))
00350 {
00351 url = url + _(_URL_PARAM_SEPARATOR);
00352 url = url + res_params->encodeSimple();
00353 }
00354
00355
00356
00357 if ((i > 0) && (item->getResource(i)->getHandlerType() == CH_EXTURL) &&
00358 ((item->getResource(i)->getOption(_(RESOURCE_CONTENT_TYPE)) ==
00359 THUMBNAIL) ||
00360 (item->getResource(i)->getOption(_(RESOURCE_CONTENT_TYPE)) ==
00361 ID3_ALBUM_ART)))
00362 {
00363 url = item->getResource(i)->getOption(_(RESOURCE_OPTION_URL));
00364 if (!string_ok(url))
00365 throw _Exception(_("missing thumbnail URL!"));
00366
00367 isExtThumbnail = true;
00368 }
00369
00371 #ifdef HAVE_ID3_ALBUMART
00372
00373 if ((i > 0) && ((item->getResource(i)->getHandlerType() == CH_ID3) ||
00374 (item->getResource(i)->getHandlerType() == CH_MP4) ||
00375 (item->getResource(i)->getHandlerType() == CH_EXTURL)))
00376 {
00377 String rct;
00378 if (item->getResource(i)->getHandlerType() == CH_EXTURL)
00379 rct = item->getResource(i)->getOption(_(RESOURCE_CONTENT_TYPE));
00380 else
00381 rct = item->getResource(i)->getParameter(_(RESOURCE_CONTENT_TYPE));
00382 if (rct == ID3_ALBUM_ART)
00383 {
00384 Ref<Element> aa(new Element(MetadataHandler::getMetaFieldName(M_ALBUMARTURI)));
00385 aa->setText(url);
00386 #ifdef EXTEND_PROTOCOLINFO
00387 if (config->getBoolOption(CFG_SERVER_EXTEND_PROTOCOLINFO))
00388 {
00391 aa->setAttribute(_("xmlns:dlna"),
00392 _("urn:schemas-dlna-org:metadata-1-0"));
00393 aa->setAttribute(_("dlna:profileID"), _("JPEG_TN"));
00394 }
00395 #endif
00396 element->appendElementChild(aa);
00397 continue;
00398 }
00399 }
00400 #endif
00401 if (!isExtThumbnail)
00402 {
00403 #ifdef EXTERNAL_TRANSCODING
00404
00405
00406
00407
00408 if (!skipURL)
00409 {
00410 if (transcoded)
00411 url = url + renderExtension(contentType, nil);
00412 else
00413 url = url + renderExtension(contentType, item->getLocation());
00414 }
00415 #else
00416
00417
00418
00419
00420 if ((i == 0) && (!skipURL))
00421 {
00422 url = url + renderExtension(contentType, item->getLocation());
00423 }
00424
00425 #endif
00426 }
00427 #ifdef EXTEND_PROTOCOLINFO
00428 if (config->getBoolOption(CFG_SERVER_EXTEND_PROTOCOLINFO))
00429 {
00430 String extend;
00431 if (contentType == CONTENT_TYPE_MP3)
00432 extend = _(D_PROFILE) + "=" + D_MP3 + ";";
00433 else if (contentType == CONTENT_TYPE_PCM)
00434 extend = _(D_PROFILE) + "=" + D_LPCM + ";";
00435 else if (contentType == CONTENT_TYPE_JPG)
00436 {
00437 String resolution = res_attrs->get(MetadataHandler::getResAttrName(R_RESOLUTION));
00438 int x;
00439 int y;
00440 if (string_ok(resolution) &&
00441 check_resolution(resolution, &x, &y))
00442 {
00443
00444 if ((i > 0) &&
00445 (((item->getResource(i)->getHandlerType() == CH_LIBEXIF) &&
00446 (item->getResource(i)->getParameter(_(RESOURCE_CONTENT_TYPE))
00447 == EXIF_THUMBNAIL)) ||
00448 (item->getResource(i)->getOption(_(RESOURCE_CONTENT_TYPE))
00449 == EXIF_THUMBNAIL) ||
00450 (item->getResource(i)->getOption(_(RESOURCE_CONTENT_TYPE))
00451 == THUMBNAIL)) &&
00452 (x <= 160) && (y <= 160))
00453 extend = _(D_PROFILE) + "=" + D_JPEG_TN+";";
00454 else if ((x <= 640) && (y <= 420))
00455 extend = _(D_PROFILE) + "=" + D_JPEG_SM+";";
00456 else if ((x <= 1024) && (y <=768))
00457 extend = _(D_PROFILE) + "=" + D_JPEG_MED+";";
00458 else if ((x <= 4096) && (y <=4096))
00459 extend = _(D_PROFILE) + "=" + D_JPEG_LRG+";";
00460 }
00461 }
00462
00463 #ifdef EXTERNAL_TRANSCODING
00464
00465
00466 if (!isExtThumbnail && transcoded)
00467 {
00468 extend = extend + D_OP + "=00;" +
00469 D_CONVERSION_INDICATOR + "=" D_CONVERSION;
00470
00471 if (mimeType.startsWith(_("audio")) ||
00472 mimeType.startsWith(_("video")))
00473 extend = extend + ";" D_FLAGS "=" D_TR_FLAGS_AV;
00474 }
00475 else
00476 #endif
00477 extend = extend + D_OP + "=01;" +
00478 D_CONVERSION_INDICATOR + "=" + D_NO_CONVERSION;
00479
00480 protocolInfo = protocolInfo.substring(0, protocolInfo.rindex(':')+1) +
00481 extend;
00482 res_attrs->put(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
00483 protocolInfo);
00484
00485 log_debug("extended protocolInfo: %s\n", protocolInfo.c_str());
00486 }
00487 #endif
00488 #ifdef EXTERNAL_TRANSCODING
00489 if (!hide_original_resource || transcoded ||
00490 (hide_original_resource && (original_resource != i)))
00491 #endif
00492 element->appendElementChild(UpnpXML_DIDLRenderResource(url, res_attrs));
00493 }
00494 }
00495
00496 Ref<CdsResourceManager::UrlBase> CdsResourceManager::addResources_getUrlBase(Ref<CdsItem> item, bool forceLocal)
00497 {
00498 Ref<Element> res;
00499
00500 Ref<UrlBase> urlBase(new UrlBase);
00502
00503 Ref<Dictionary> dict(new Dictionary());
00504 dict->put(_(URL_OBJECT_ID), String::from(item->getID()));
00505
00506 urlBase->addResID = false;
00509 int objectType = item->getObjectType();
00510 if (IS_CDS_ITEM_INTERNAL_URL(objectType))
00511 {
00512 urlBase->urlBase = Server::getInstance()->getVirtualURL() +
00513 _(_URL_PARAM_SEPARATOR) +
00514 CONTENT_SERVE_HANDLER +
00515 _(_URL_PARAM_SEPARATOR) + item->getLocation();
00516 return urlBase;
00517 }
00518
00519 if (IS_CDS_ITEM_EXTERNAL_URL(objectType))
00520 {
00521 if (!item->getFlag(OBJECT_FLAG_PROXY_URL) && (!forceLocal))
00522 {
00523 urlBase->urlBase = item->getLocation();
00524 return urlBase;
00525 }
00526
00527 if ((item->getFlag(OBJECT_FLAG_ONLINE_SERVICE) &&
00528 item->getFlag(OBJECT_FLAG_PROXY_URL)) || forceLocal)
00529 {
00530 urlBase->urlBase = Server::getInstance()->getVirtualURL() +
00531 _(_URL_PARAM_SEPARATOR) +
00532 CONTENT_ONLINE_HANDLER + _(_URL_PARAM_SEPARATOR) +
00533 dict->encodeSimple() + _(_URL_PARAM_SEPARATOR) +
00534 _(URL_RESOURCE_ID) + _(_URL_PARAM_SEPARATOR);
00535 urlBase->addResID = true;
00536 return urlBase;
00537 }
00538 }
00539
00540 #ifdef HAVE_LIBDVDNAV_DISABLED
00541 if (IS_CDS_ITEM(objectType) && (item->getFlag(OBJECT_FLAG_DVD_IMAGE)))
00542 {
00543 urlBase->urlBase = Server::getInstance()->getVirtualURL() +
00544 _(_URL_PARAM_SEPARATOR) +
00545 CONTENT_DVD_IMAGE_HANDLER + _(_URL_PARAM_SEPARATOR) +
00546 dict->encodeSimple() + _(_URL_PARAM_SEPARATOR) +
00547 _(URL_RESOURCE_ID) + _(_URL_PARAM_SEPARATOR);
00548 urlBase->addResID = true;
00549 return urlBase;
00550 }
00551 #endif
00552
00553 urlBase->urlBase = Server::getInstance()->getVirtualURL() +
00554 _(_URL_PARAM_SEPARATOR) +
00555 CONTENT_MEDIA_HANDLER + _(_URL_PARAM_SEPARATOR) +
00556 dict->encodeSimple() + _(_URL_PARAM_SEPARATOR) +
00557 _(URL_RESOURCE_ID) + _(_URL_PARAM_SEPARATOR);
00558 urlBase->addResID = true;
00559 return urlBase;
00560 }
00561
00562 String CdsResourceManager::getFirstResource(Ref<CdsItem> item)
00563 {
00564 Ref<UrlBase> urlBase = addResources_getUrlBase(item);
00565
00566 if (urlBase->addResID)
00567 return urlBase->urlBase + 0;
00568 else
00569 return urlBase->urlBase;
00570 }