sbDeviceTranscoding.cpp
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2011 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 #include "sbDeviceTranscoding.h"
26 
27 // Mozilla includes
28 #include <nsArrayUtils.h>
29 #include <nsComponentManagerUtils.h>
30 #include <nsIFileURL.h>
31 #include <nsIInputStream.h>
32 #include <nsIIOService.h>
33 #include <nsIStringEnumerator.h>
34 #include <nsISupportsPrimitives.h>
35 #include <nsIWritablePropertyBag2.h>
36 #include <nsIVariant.h>
37 
38 #include <nsServiceManagerUtils.h>
39 
40 // Songbird interfaces
41 #include <sbIDeviceEvent.h>
42 #include <sbIJobCancelable.h>
43 #include <sbIMediacoreEventTarget.h>
44 #include <sbIMediaFormatMutable.h>
45 #include <sbIMediaInspector.h>
46 #include <sbITranscodeAlbumArt.h>
47 #include <sbITranscodeError.h>
48 #include <sbITranscodeManager.h>
49 #include <sbITranscodeVideoJob.h>
50 #include <sbITranscodingConfigurator.h>
51 
52 // Songbird includes
54 #include <sbStandardProperties.h>
55 #include <sbStringUtils.h>
56 #include <sbTranscodeUtils.h>
57 #include <sbVariantUtils.h>
58 
59 // Local includes
60 #include "sbDeviceStatusHelper.h"
61 #include "sbDeviceUtils.h"
63 
64 /*
65  * To log this module, set the following environment variable:
66  * NSPR_LOG_MODULES=sbBaseDevice:5
67  */
68 #undef LOG
69 #undef TRACE
70 #ifdef PR_LOGGING
71 extern PRLogModuleInfo* gBaseDeviceLog;
72 #define LOG(args) PR_LOG(gBaseDeviceLog, PR_LOG_WARN, args)
73 #define TRACE(args) PR_LOG(gBaseDeviceLog, PR_LOG_DEBUG, args)
74 #else
75 #define LOG(args) do{ } while(0)
76 #define TRACE(args) do { } while(0)
77 #endif
78 
79 sbDeviceTranscoding::sbDeviceTranscoding(sbBaseDevice * aBaseDevice) :
80  mBaseDevice(aBaseDevice)
81 {
82 }
83 
84 nsresult
86  nsIArray **aSupportedProfiles)
87 {
88  nsresult rv;
89  if (!mTranscodeProfiles) {
91  aType,
92  mBaseDevice,
93  getter_AddRefs(mTranscodeProfiles));
94  NS_ENSURE_SUCCESS(rv, rv);
95  }
96 
97  NS_IF_ADDREF(*aSupportedProfiles = mTranscodeProfiles);
98 
99  return NS_OK;
100 }
101 
102 nsresult
104  sbITranscodeProfile **aProfile)
105 {
106  nsresult rv;
107 
108  PRBool hasProfilePref = PR_FALSE;
109  // See if we have a preference for the transcoding profile.
110  nsCOMPtr<nsIVariant> profileIdVariant;
111  nsString prefProfileId;
112  rv = mBaseDevice->GetPreference(
113  NS_LITERAL_STRING("transcode_profile.profile_id"),
114  getter_AddRefs(profileIdVariant));
115  if (NS_SUCCEEDED(rv)) {
116  PRUint16 dataType = 0;
117  rv = profileIdVariant->GetDataType(&dataType);
118  if (NS_SUCCEEDED(rv) &&
119  dataType != nsIDataType::VTYPE_EMPTY &&
120  dataType != nsIDataType::VTYPE_VOID) {
121  hasProfilePref = PR_TRUE;
122  rv = profileIdVariant->GetAsAString(prefProfileId);
123  NS_ENSURE_SUCCESS(rv, rv);
124  TRACE(("%s: found a profile", __FUNCTION__));
125  }
126  }
127 
128  nsCOMPtr<nsIArray> supportedProfiles;
129  rv = mBaseDevice->GetSupportedTranscodeProfiles(transcodeType,
130  getter_AddRefs(supportedProfiles));
131  NS_ENSURE_SUCCESS(rv, rv);
132 
133  PRUint32 bestPriority = 0;
134  nsCOMPtr<sbITranscodeProfile> bestProfile;
135  nsCOMPtr<sbITranscodeProfile> prefProfile;
136 
137  PRUint32 length;
138  rv = supportedProfiles->GetLength(&length);
139  NS_ENSURE_SUCCESS(rv, rv);
140 
141  for (PRUint32 index = 0; index < length; ++index) {
142  nsCOMPtr<sbITranscodeProfile> profile =
143  do_QueryElementAt(supportedProfiles, index, &rv);
144  NS_ENSURE_SUCCESS(rv, rv);
145 
146  if (profile) {
147  PRUint32 profileContentType;
148  rv = profile->GetType(&profileContentType);
149  NS_ENSURE_SUCCESS(rv, rv);
150 
151  // If the content types don't match, skip (we don't want to use a video
152  // transcoding profile to transcode audio, for example)
153  if (profileContentType == transcodeType) {
154  if (hasProfilePref) {
155  nsString profileId;
156  rv = profile->GetId(profileId);
157  NS_ENSURE_SUCCESS(rv, rv);
158 
159  if (profileId.Equals(prefProfileId))
160  prefProfile = profile;
161  }
162 
163  // Also track the highest-priority profile. This is our default.
164  PRUint32 priority;
165  rv = profile->GetPriority(&priority);
166  NS_ENSURE_SUCCESS(rv, rv);
167 
168  if (!bestProfile || priority > bestPriority) {
169  bestProfile = profile;
170  bestPriority = priority;
171  }
172  }
173  else {
174  TRACE(("%s: skipping profile for content type %d",
175  __FUNCTION__,
176  profileContentType));
177  }
178  }
179  }
180  if (prefProfile) {
181  // We found the profile selected in the preferences. Apply relevant
182  // preferenced properties to it as well...
183  nsCOMPtr<nsIArray> audioProperties;
184  rv = prefProfile->GetAudioProperties(getter_AddRefs(audioProperties));
185  NS_ENSURE_SUCCESS(rv, rv);
187  mBaseDevice,
188  audioProperties,
189  NS_LITERAL_STRING("transcode_profile.audio_properties"));
190  NS_ENSURE_SUCCESS(rv, rv);
191 
192  nsCOMPtr<nsIArray> videoProperties;
193  rv = prefProfile->GetVideoProperties(getter_AddRefs(videoProperties));
194  NS_ENSURE_SUCCESS(rv, rv);
196  mBaseDevice,
197  videoProperties,
198  NS_LITERAL_STRING("transcode_profile.video_properties"));
199  NS_ENSURE_SUCCESS(rv, rv);
200 
201  nsCOMPtr<nsIArray> containerProperties;
202  rv = prefProfile->GetContainerProperties(
203  getter_AddRefs(containerProperties));
204  NS_ENSURE_SUCCESS(rv, rv);
206  mBaseDevice,
207  containerProperties,
208  NS_LITERAL_STRING("transcode_profile.container_properties"));
209  NS_ENSURE_SUCCESS(rv, rv);
210 
211  prefProfile.forget(aProfile);
212  TRACE(("%s: found pref profile", __FUNCTION__));
213  return NS_OK;
214  }
215  else if (bestProfile) {
216  TRACE(("%s: using best-match profile", __FUNCTION__));
217  bestProfile.forget(aProfile);
218  return NS_OK;
219  }
220 
221  // Indicate no appropriate transcoding profile available
222  TRACE(("%s: no supported profiles available", __FUNCTION__));
223  return NS_ERROR_NOT_AVAILABLE;
224 }
225 
231 nsresult
233 {
234  TRACE(("%s", __FUNCTION__));
235 
236  nsresult rv;
237 
238  if (aBatch.empty()) {
239  return NS_OK;
240  }
241 
242  nsCOMPtr<nsIArray> imageFormats;
243  rv = mBaseDevice->GetSupportedAlbumArtFormats(getter_AddRefs(imageFormats));
244  // No album art formats isn't fatal.
245  if (rv != NS_ERROR_NOT_AVAILABLE) {
246  NS_ENSURE_SUCCESS(rv, rv);
247  }
248 
249  // Iterate over the batch getting the transcode profiles if needed.
250  const Batch::const_iterator end = aBatch.end();
251  for (Batch::const_iterator iter = aBatch.begin();
252  iter != end;
253  ++iter) {
254  TransferRequest * request = static_cast<TransferRequest*>(*iter);
255 
256  // Check for abort.
257  if (mBaseDevice->IsRequestAborted()) {
258  return NS_ERROR_ABORT;
259  }
260 
261  // We only want non-playlist write requests
262  if (request->GetType() != sbIDevice::REQUEST_WRITE ||
263  request->IsPlaylist())
264  continue;
265 
266  // First, ensure that the item isn't DRM protected before looking for
267  // transcode profiles.
268  if (sbDeviceUtils::IsItemDRMProtected(request->item)) {
269  PRBool isSupported = PR_FALSE;
270  rv = mBaseDevice->SupportsMediaItemDRM(
271  request->item,
272  PR_TRUE, // report errors
273  &isSupported);
274  if (NS_SUCCEEDED(rv) && isSupported) {
275  request->destinationCompatibility =
277  }
278  else {
279  request->destinationCompatibility =
281  }
282  }
283  else {
284  rv = FindTranscodeProfile(request->item,
285  &request->transcodeProfile,
286  &request->destinationCompatibility);
287  // Treat no profiles available as not needing transcoding
288  if (NS_FAILED(rv)) {
289  TRACE(("%s: no transcode profile available", __FUNCTION__));
290  request->destinationCompatibility =
292  }
293  if (request->transcodeProfile) {
294  TRACE(("%s: transcoding needed", __FUNCTION__));
295  request->destinationCompatibility =
297  }
298 
299  request->albumArt = do_CreateInstance(
301  NS_ENSURE_SUCCESS(rv, rv);
302 
303  // It's ok for this to fail; album art is optional
304  rv = request->albumArt->Init(request->item, imageFormats);
305  if (NS_FAILED(rv)) {
306  TRACE(("%s: no album art available", __FUNCTION__));
307  request->albumArt = nsnull;
308  }
309  }
310  }
311 
312  return NS_OK;
313 }
314 
315 PRUint32
317 {
318  nsresult rv;
319 
320  nsString contentType;
321  rv = aMediaItem->GetContentType(contentType);
322  NS_ENSURE_SUCCESS(rv, sbITranscodeProfile::TRANSCODE_TYPE_UNKNOWN);
323 
324  if (contentType.Equals(NS_LITERAL_STRING("audio"))) {
326  }
327  else if (contentType.Equals(NS_LITERAL_STRING("video"))) {
329  }
330  else if (contentType.Equals(NS_LITERAL_STRING("image"))) {
332  }
333  NS_WARNING("sbDeviceUtils::GetTranscodeType: "
334  "returning unknown transcoding type");
336 }
337 
343 static nsresult
345  sbBaseDevice* aDevice)
346 {
347  NS_ENSURE_ARG_POINTER(aError);
348  NS_ENSURE_ARG_POINTER(aDevice);
349 
350  nsresult rv;
351 
352  nsCOMPtr<nsIWritablePropertyBag2> bag =
353  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
354  NS_ENSURE_SUCCESS(rv, rv);
355  nsCOMPtr<nsISupportsString> errorString(do_QueryInterface(aError));
356  NS_ENSURE_TRUE(errorString, NS_ERROR_NO_INTERFACE);
357  nsString message;
358  rv = errorString->GetData(message);
359  if (NS_SUCCEEDED(rv)) {
360  rv = bag->SetPropertyAsAString(NS_LITERAL_STRING("message"),
361  message);
362  NS_ENSURE_SUCCESS(rv, rv);
363  }
364  rv = bag->SetPropertyAsInterface(NS_LITERAL_STRING("transcode-error"),
365  NS_ISUPPORTS_CAST(sbITranscodeError*, aError));
366  NS_ENSURE_SUCCESS(rv, rv);
367 
368  aDevice->CreateAndDispatchEvent(
370  sbNewVariant(bag));
371  /* ignore result */
372  return NS_OK;
373 }
374 
375 nsresult
377  sbITranscodeProfile ** aProfile,
378  CompatibilityType * aDeviceCompatibility)
379 {
380  TRACE(("%s", __FUNCTION__));
381  NS_ENSURE_ARG_POINTER(aMediaItem);
382  NS_ENSURE_ARG_POINTER(aProfile);
383  NS_ENSURE_ARG_POINTER(aDeviceCompatibility);
384 
385  nsresult rv;
386  *aProfile = nsnull;
387  *aDeviceCompatibility = TransferRequest::COMPAT_UNSUPPORTED;
388 
389  if (sbDeviceUtils::IsItemDRMProtected(aMediaItem)) {
390  // Transcoding from DRM formats is not supported.
391  return NS_ERROR_NOT_AVAILABLE;
392  }
393 
394  PRUint32 const transcodeType = GetTranscodeType(aMediaItem);
395  bool needsTranscoding = false;
396 
397  nsCOMPtr<sbIMediaFormat> mediaFormat;
398  rv = GetMediaFormat(transcodeType, aMediaItem, getter_AddRefs(mediaFormat));
399  if (NS_FAILED(rv)) {
400  nsString inputUri;
401  nsresult rv2;
402  rv2 = aMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
403  inputUri);
404  NS_ENSURE_SUCCESS(rv2, rv2);
405  nsTArray<nsString> params;
406  params.AppendElement(inputUri);
407  SBLocalizedString message("transcode.error.generic", params);
408  nsCOMPtr<sbITranscodeError> error;
409  rv2 = SB_NewTranscodeError(message,
410  message,
411  SBVoidString(),
412  inputUri,
413  aMediaItem,
414  getter_AddRefs(error));
415  NS_ENSURE_SUCCESS(rv2, rv2);
416  rv2 = DispatchTranscodeError(error, mBaseDevice);
417  NS_ENSURE_SUCCESS(rv2, rv2);
418  }
419  if (rv == NS_ERROR_NOT_AVAILABLE) {
420  return rv;
421  }
422  NS_ENSURE_SUCCESS(rv, rv);
423  rv = sbDeviceUtils::DoesItemNeedTranscoding(transcodeType,
424  mediaFormat,
425  mBaseDevice,
426  needsTranscoding);
427  NS_ENSURE_SUCCESS(rv, rv);
428 
429  if (!needsTranscoding) {
430  *aDeviceCompatibility = TransferRequest::COMPAT_SUPPORTED;
431  return NS_OK;
432  }
433 
434  nsCOMPtr<sbIDeviceTranscodingConfigurator> configurator;
436  getter_AddRefs(configurator));
437  NS_ENSURE_SUCCESS(rv, rv);
438 
439  nsCOMPtr<nsIURI> inputUri;
440  rv = aMediaItem->GetContentSrc(getter_AddRefs(inputUri));
441  NS_ENSURE_SUCCESS(rv, rv);
442  rv = configurator->SetInputUri(inputUri);
443  NS_ENSURE_SUCCESS(rv, rv);
444  nsCOMPtr<sbIDevice> device =
445  do_QueryInterface(NS_ISUPPORTS_CAST(sbIDevice*, mBaseDevice), &rv);
446  NS_ENSURE_SUCCESS(rv, rv);
447  rv = configurator->SetDevice(device);
448  NS_ENSURE_SUCCESS(rv, rv);
449 
450  rv = configurator->DetermineOutputType();
451  if (NS_SUCCEEDED(rv)) {
452  *aDeviceCompatibility = TransferRequest::COMPAT_NEEDS_TRANSCODING;
453  }
454  else {
455  // we need transcoding, but we don't have anything available to do it with
456  nsCOMPtr<sbITranscodeError> error;
457  rv = configurator->GetLastError(getter_AddRefs(error));
458  if (NS_SUCCEEDED(rv) && error) {
459  rv = error->SetDestItem(aMediaItem);
460  NS_ENSURE_SUCCESS(rv, rv);
461  rv = DispatchTranscodeError(error, mBaseDevice);
462  NS_ENSURE_SUCCESS(rv, rv);
463  }
464  }
465  return NS_OK;
466 }
467 
468 nsresult
470  sbIMediaFormat** aMediaFormat)
471 {
472  nsresult rv;
473  nsCOMPtr<sbIMediaFormatMutable> format;
474  nsCOMPtr<sbIMediaFormatAudioMutable> audioFormat;
475  nsCOMPtr<sbIMediaFormatContainerMutable> containerFormat;
476 
477  audioFormat = do_CreateInstance(SB_MEDIAFORMATAUDIO_CONTRACTID, &rv);
478  NS_ENSURE_SUCCESS (rv, rv);
479 
480  struct sbExtensionToContentFormatEntry_t formatInfo;
481  PRUint32 rate = 0;
482  PRUint32 channels = 0;
483  PRUint32 bitrate = 0;
484  rv = sbDeviceUtils::GetFormatTypeForItem(aMediaItem,
485  formatInfo,
486  rate,
487  channels,
488  bitrate);
489  if (NS_FAILED(rv)) {
490  // Fill out the two fields we use - this will mean that we have a format
491  // object that certainly won't be supported anywhere, so we can then fall
492  // back to transcoding, etc.
493  formatInfo.Codec = "audio/x-unknown";
494  formatInfo.ContainerFormat = "application/x-unknown";
495  }
496 
497  rv = audioFormat->SetAudioType(NS_ConvertASCIItoUTF16(formatInfo.Codec));
498  NS_ENSURE_SUCCESS (rv, rv);
499 
500  if (!rate) {
501  // If we didn't get the sample rate, just default to 44100, as that's the
502  // most common. We probably just don't have a sample rate because the
503  // metadata importer couldn't figure it out, and we shouldn't reject a file
504  // just because of this.
505  rate = 44100;
506  }
507  rv = audioFormat->SetSampleRate (rate);
508  NS_ENSURE_SUCCESS (rv, rv);
509 
510  if (!channels) {
511  // Likewise for channels, default to stereo.
512  channels = 2;
513  }
514  rv = audioFormat->SetChannels (channels);
515  NS_ENSURE_SUCCESS (rv, rv);
516 
517  // Not setting bitrate is more or less ok - so we don't use a default here.
518  if (bitrate) {
519  rv = audioFormat->SetBitRate (bitrate);
520  NS_ENSURE_SUCCESS (rv, rv);
521  }
522 
523  containerFormat = do_CreateInstance(SB_MEDIAFORMATCONTAINER_CONTRACTID, &rv);
524  NS_ENSURE_SUCCESS (rv, rv);
525 
526  containerFormat->SetContainerType(
527  NS_ConvertASCIItoUTF16(formatInfo.ContainerFormat));
528  NS_ENSURE_SUCCESS (rv, rv);
529 
530  format = do_CreateInstance(SB_MEDIAFORMAT_CONTRACTID, &rv);
531  NS_ENSURE_SUCCESS (rv, rv);
532 
533  rv = format->SetContainer (containerFormat);
534  NS_ENSURE_SUCCESS (rv, rv);
535  rv = format->SetAudioStream (audioFormat);
536  NS_ENSURE_SUCCESS (rv, rv);
537 
538  rv = CallQueryInterface(format.get(), aMediaFormat);
539  NS_ENSURE_SUCCESS (rv, rv);
540 
541  return NS_OK;
542 }
543 
544 nsresult
545 sbDeviceTranscoding::GetMediaFormat(PRUint32 aTranscodeType,
546  sbIMediaItem* aMediaItem,
547  sbIMediaFormat** aMediaFormat)
548 {
549  nsresult rv;
550 
551  if (aTranscodeType == sbITranscodeProfile::TRANSCODE_TYPE_AUDIO)
552  {
553  // TODO: if this fails, we could fall back to using the inspector?
554  rv = GetAudioFormatFromMediaItem(aMediaItem, aMediaFormat);
555  NS_ENSURE_SUCCESS(rv, rv);
556 
557  return NS_OK;
558  }
559  else {
560  if (!mMediaInspector) {
561  mMediaInspector = do_CreateInstance(SB_MEDIAINSPECTOR_CONTRACTID, &rv);
562  NS_ENSURE_SUCCESS(rv, rv);
563  }
564  nsCOMPtr<sbIMediaFormat> mediaFormat;
565  rv = mMediaInspector->InspectMedia(aMediaItem, getter_AddRefs(mediaFormat));
566  NS_ENSURE_SUCCESS(rv, rv);
567 
568  mediaFormat.forget(aMediaFormat);
569  return NS_OK;
570  }
571 }
572 
573 nsresult
575 {
576  nsresult rv;
577  if (!mMediaInspector) {
578  mMediaInspector = do_CreateInstance(SB_MEDIAINSPECTOR_CONTRACTID, &rv);
579  NS_ENSURE_SUCCESS(rv, rv);
580  }
581  NS_ADDREF(*_retval = mMediaInspector);
582  return NS_OK;
583 }
584 
585 nsresult
587  sbIMediaItem *aMediaItem,
588  sbDeviceStatusHelper * aDeviceStatusHelper,
589  nsIURI * aDestinationURI,
590  nsIURI ** aTranscodedDestinationURI)
591 {
592  NS_ENSURE_ARG_POINTER(aMediaItem);
593  NS_ENSURE_ARG_POINTER(aDeviceStatusHelper);
594  NS_ENSURE_ARG_POINTER(aDestinationURI);
595 
596  // Function variables.
597  nsresult rv;
598 
599  // Create a transcode job.
600  nsCOMPtr<nsISupports> tcJob;
601  nsCOMPtr<sbITranscodeManager> txMgr;
602  rv = GetTranscodeManager(getter_AddRefs(txMgr));
603  NS_ENSURE_SUCCESS(rv, rv);
604  rv = txMgr->GetTranscoderForMediaItem(aMediaItem,
605  getter_AddRefs(tcJob));
606  NS_ENSURE_SUCCESS(rv, rv);
607 
608  nsCOMPtr<nsIThread> target;
609  rv = NS_GetMainThread(getter_AddRefs(target));
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  nsCOMPtr<nsIIOService> ioService =
613  do_ProxiedGetService("@mozilla.org/network/io-service;1", &rv);
614  NS_ENSURE_SUCCESS(rv, rv);
615 
616  nsCOMPtr<nsIURI> transcodedDestinationURI;
617  nsCOMPtr<nsIURI> transcodedDestinationURIProxy;
618  rv = ioService->NewURI(NS_LITERAL_CSTRING(""),
619  nsnull,
620  aDestinationURI,
621  getter_AddRefs(transcodedDestinationURI));
622  NS_ENSURE_SUCCESS(rv, rv);
623  rv = do_GetProxyForObject(target,
624  NS_GET_IID(nsIURI),
625  transcodedDestinationURI,
626  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
627  getter_AddRefs(transcodedDestinationURIProxy));
628  NS_ENSURE_SUCCESS(rv, rv);
629  transcodedDestinationURI = transcodedDestinationURIProxy;
630 
631  nsCOMPtr<sbITranscodeVideoJob> transcodeJob = do_QueryInterface(tcJob, &rv);
632  NS_ENSURE_SUCCESS(rv, rv);
633  nsCOMPtr<sbITranscodeVideoJob> proxyTranscodeJob;
634  rv = do_GetProxyForObject(target,
635  NS_GET_IID(sbITranscodeVideoJob),
636  tcJob,
637  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
638  getter_AddRefs(proxyTranscodeJob));
639  NS_ENSURE_SUCCESS(rv, rv);
640 
641  transcodeJob.swap(proxyTranscodeJob);
642 
643  PRUint32 const transcodeType = GetTranscodeType(aMediaItem);
644 
645  nsCString destinationURISpec;
646  rv = aDestinationURI->GetSpec(destinationURISpec);
647  NS_ENSURE_SUCCESS(rv, rv);
648  rv = transcodeJob->SetDestURI(NS_ConvertUTF8toUTF16(destinationURISpec));
649  NS_ENSURE_SUCCESS(rv, rv);
650 
651  nsCOMPtr<nsIURI> sourceURI;
652  rv = aMediaItem->GetContentSrc(getter_AddRefs(sourceURI));
653  NS_ENSURE_SUCCESS(rv, rv);
654  nsCString sourceURISpec;
655  rv = sourceURI->GetSpec(sourceURISpec);
656  NS_ENSURE_SUCCESS(rv, rv);
657  rv = transcodeJob->SetSourceURI(NS_ConvertUTF8toUTF16(sourceURISpec));
658  NS_ENSURE_SUCCESS(rv, rv);
659 
660  nsCOMPtr<sbIPropertyArray> metadata;
661  rv = aMediaItem->GetProperties(nsnull, getter_AddRefs(metadata));
662  NS_ENSURE_SUCCESS(rv, rv);
663  rv = transcodeJob->SetMetadata(metadata);
664  NS_ENSURE_SUCCESS(rv, rv);
665 
666  // Setup the configurator
667  nsCOMPtr<sbIDeviceTranscodingConfigurator> configurator;
669  getter_AddRefs(configurator));
670  NS_ENSURE_SUCCESS(rv, rv);
671 
672  rv = configurator->SetInputUri(sourceURI);
673  NS_ENSURE_SUCCESS(rv, rv);
674  nsCOMPtr<sbIDevice> device =
675  do_QueryInterface(NS_ISUPPORTS_CAST(sbIDevice*, mBaseDevice), &rv);
676  NS_ENSURE_SUCCESS(rv, rv);
677  rv = configurator->SetDevice(device);
678  NS_ENSURE_SUCCESS(rv, rv);
679 
680  nsCOMPtr<sbITranscodingConfigurator> qiConfigurator =
681  do_QueryInterface(configurator, &rv);
682  NS_ENSURE_SUCCESS(rv, rv);
683 
684  rv = transcodeJob->SetConfigurator(qiConfigurator);
685  NS_ENSURE_SUCCESS(rv, rv);
686 
687  /* Set up album art for output to resulting file - transcoding will be
688  performed if required. Ignore failures here - album art isn't essential */
689  nsCOMPtr<sbITranscodeAlbumArt> albumArt = do_CreateInstance(
691  NS_ENSURE_SUCCESS(rv, rv);
692 
693  nsCOMPtr<nsIArray> imageFormats;
694  rv = mBaseDevice->GetSupportedAlbumArtFormats(getter_AddRefs(imageFormats));
695  // No album art formats isn't fatal.
696  if (rv != NS_ERROR_NOT_AVAILABLE) {
697  NS_ENSURE_SUCCESS(rv, rv);
698  }
699 
700  rv = albumArt->Init(aMediaItem, imageFormats);
701  if (NS_SUCCEEDED (rv)) {
702  nsCOMPtr<nsIInputStream> imageStream;
703  rv = albumArt->GetTranscodedArt(getter_AddRefs(imageStream));
704  if (imageStream && NS_SUCCEEDED(rv)) {
705  rv = transcodeJob->SetMetadataImage(imageStream);
706  NS_ENSURE_SUCCESS(rv, rv);
707  }
708  }
709 
710  nsCOMPtr<sbIJobCancelable> cancel = do_QueryInterface(tcJob);
711 
712  PRMonitor * const stopMonitor =
713  mBaseDevice->mRequestThreadQueue->GetStopWaitMonitor();
714  NS_ENSURE_TRUE(stopMonitor, NS_ERROR_UNEXPECTED);
715 
716  // Create our listener for transcode progress.
717  nsRefPtr<sbTranscodeProgressListener> listener =
719  aDeviceStatusHelper,
720  aMediaItem,
721  stopMonitor,
723  cancel);
724  NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
725 
726  // Setup the progress listener.
727  nsCOMPtr<sbIJobProgress> progress = do_QueryInterface(tcJob, &rv);
728  NS_ENSURE_SUCCESS(rv, rv);
729  nsCOMPtr<sbIJobProgress> proxiedProgress;
730  rv = do_GetProxyForObject(target,
731  NS_GET_IID(sbIJobProgress),
732  progress,
733  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
734  getter_AddRefs(proxiedProgress));
735  NS_ENSURE_SUCCESS(rv, rv);
736  rv = proxiedProgress->AddJobProgressListener(listener);
737  NS_ENSURE_SUCCESS(rv, rv);
738 
739  // Setup the mediacore event listener.
740  nsCOMPtr<sbIMediacoreEventTarget> eventTarget = do_QueryInterface(tcJob,
741  &rv);
742  NS_ENSURE_SUCCESS(rv, rv);
743  rv = eventTarget->AddListener(listener);
744  NS_ENSURE_SUCCESS(rv, rv);
745 
746  // This is async.
747  rv = transcodeJob->Transcode();
748  NS_ENSURE_SUCCESS(rv, rv);
749 
750  // Wait until the transcode job is complete.
751  //XXXeps should check for abort. To do this, the job will have to be
752  // canceled.
753  PRBool isComplete = PR_FALSE;
754  while (!isComplete) {
755  // Operate within the request wait monitor.
756  nsAutoMonitor monitor(stopMonitor);
757 
758  // Check if the job is complete.
759  isComplete = listener->IsComplete();
760 
761  // If not complete, wait for completion.
762  if (!isComplete)
763  monitor.Wait();
764  }
765 
766  // Get the transcoded video file URI.
767  nsAutoString destURI;
768  rv = transcodeJob->GetDestURI(destURI);
769  NS_ENSURE_SUCCESS(rv, rv);
770  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(destURI),
771  nsnull,
772  nsnull,
773  getter_AddRefs(transcodedDestinationURI));
774  NS_ENSURE_SUCCESS(rv, rv);
775  rv = do_GetProxyForObject(target,
776  NS_GET_IID(nsIURI),
777  transcodedDestinationURI,
778  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
779  getter_AddRefs(transcodedDestinationURIProxy));
780  NS_ENSURE_SUCCESS(rv, rv);
781  transcodedDestinationURI = transcodedDestinationURIProxy;
782 
783  if (aTranscodedDestinationURI)
784  transcodedDestinationURI.forget(aTranscodedDestinationURI);
785 
786  // If we abort, delete the destination file.
787  if (listener->IsAborted()) {
788  nsCOMPtr<nsIFileURL> fileURL =
789  do_QueryInterface(transcodedDestinationURIProxy);
790  if (fileURL) {
791  nsCOMPtr<nsIFile> file;
792  rv = fileURL->GetFile(getter_AddRefs(file));
793  if(NS_SUCCEEDED(rv)) {
794  rv = file->Remove(PR_FALSE);
795  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
796  "Failed to remove temporary file used for transcoding");
797  }
798  }
799  return NS_ERROR_ABORT;
800  }
801 
802  // Check the transcode status.
803  PRUint16 status;
804  rv = progress->GetStatus(&status);
805  NS_ENSURE_SUCCESS(rv, rv);
806 
807  // Log any errors.
808 #ifdef PR_LOGGING
809  if (status != sbIJobProgress::STATUS_SUCCEEDED) {
810  // Get an enumerator of the error messages.
811  nsCOMPtr<nsIStringEnumerator> errorMessageEnum;
812  rv = progress->GetErrorMessages(getter_AddRefs(errorMessageEnum));
813 
814  // Log each error.
815  if (NS_SUCCEEDED(rv)) {
816  PRBool hasMore;
817  rv = errorMessageEnum->HasMore(&hasMore);
818  if (NS_FAILED(rv))
819  hasMore = PR_FALSE;
820  while (hasMore) {
821  // Get the next error message.
822  nsAutoString errorMessage;
823  rv = errorMessageEnum->GetNext(errorMessage);
824  if (NS_FAILED(rv))
825  break;
826 
827  // Log the error message.
828  LOG(("sbMSCDeviceBase::ReqTranscodeWrite error %s\n",
829  NS_ConvertUTF16toUTF8(errorMessage).get()));
830 
831  // Check for more error messages.
832  rv = errorMessageEnum->HasMore(&hasMore);
833  if (NS_FAILED(rv))
834  hasMore = PR_FALSE;
835  }
836  }
837  }
838 #endif
839 
840  // Check for transcode errors.
841  // There is no need to fire a device event, because any appropriate events
842  // would have been fired based on the media core event in
843  // sbTranscodeProgressListener::OnMediacoreEvent
844  NS_ENSURE_TRUE(status == sbIJobProgress::STATUS_SUCCEEDED, NS_ERROR_FAILURE);
845 
846  return NS_OK;
847 }
848 
849 nsresult sbDeviceTranscoding::GetTranscodeManager(
850  sbITranscodeManager ** aTranscodeManager)
851 {
852  nsresult rv;
853  if (!mTranscodeManager) {
854  // Get the transcode manager.
855  mTranscodeManager =
857  ("@songbirdnest.com/Songbird/Mediacore/TranscodeManager;1", &rv);
858  NS_ENSURE_SUCCESS(rv, rv);
859  }
860 
861  *aTranscodeManager = mTranscodeManager;
862  NS_ADDREF(*aTranscodeManager);
863 
864  return NS_OK;
865 }
const unsigned long TRANSCODE_TYPE_IMAGE
NS_IMETHOD GetPreference(const nsAString &aPrefName, nsIVariant **_retval)
nsresult GetMediaInspector(sbIMediaInspector **_retval)
nsresult CreateAndDispatchEvent(PRUint32 aType, nsIVariant *aData, PRBool aAsync=PR_TRUE, sbIDeviceEventTarget *aTarget=nsnull)
return NS_OK
nsresult GetAudioFormatFromMediaItem(sbIMediaItem *aMediaItem, sbIMediaFormat **aMediaFormat)
nsRefPtr< sbDeviceRequestThreadQueue > mRequestThreadQueue
Definition: sbBaseDevice.h:730
nsresult SB_NewTranscodeError(const nsAString &aMessageWithItem, const nsAString &aMessageWithoutItem, const nsAString &aDetails, const nsAString &aUri, sbIMediaItem *aMediaItem, sbITranscodeError **_retval)
char const * Codec
Definition: sbDeviceUtils.h:58
#define LOG(args)
Generic interface for exposing long running jobs to the UI.
PRUint32 GetType() const
Definition: sbRequestItem.h:65
An object capable of transcoding a source URI to a destination file.
static bool IsItemDRMProtected(sbIMediaItem *aMediaItem)
const NS_ERROR_ABORT
CompatibilityType destinationCompatibility
Definition: sbBaseDevice.h:180
const unsigned long TRANSCODE_TYPE_AUDIO
const unsigned long TRANSCODE_TYPE_UNKNOWN
An object defining a transcoding profile.
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
Songbird Variant Utility Definitions.
var ioService
static nsresult ApplyPropertyPreferencesToProfile(sbIDevice *aDevice, nsIArray *aPropertyArray, nsString aPrefNameBase)
static nsresult GetSupportedTranscodeProfiles(PRUint32 aType, sbIDevice *aDevice, nsIArray **aProfiles)
nsresult TranscodeMediaItem(sbIMediaItem *aItem, sbDeviceStatusHelper *aDeviceStatusHelper, nsIURI *aDestinationURI, nsIURI **aTranscodedDestinationURI=nsnull)
const unsigned long TRANSCODE_TYPE_AUDIO_VIDEO
virtual PRBool IsRequestAborted()
const_iterator end() const
nsresult GetMediaFormat(PRUint32 aTranscodeType, sbIMediaItem *aMediaItem, sbIMediaFormat **aMediaFormat)
#define SONGBIRD_TRANSCODEALBUMART_CONTRACTID
const unsigned long REQUEST_WRITE
Definition: sbIDevice.idl:263
const sbCreateProxiedComponent do_ProxiedGetService(const nsCID &aCID, nsresult *error=0)
RequestItems::const_iterator const_iterator
const unsigned long EVENT_DEVICE_TRANSCODE_ERROR
#define SB_MEDIAFORMATCONTAINER_CONTRACTID
nsresult FindTranscodeProfile(sbIMediaItem *aMediaItem, sbITranscodeProfile **aProfile, CompatibilityType *aDeviceCompatibility)
nsresult PrepareBatchForTranscoding(Batch &aBatch)
aBatch The batch to process
static nsresult GetFormatTypeForItem(sbIMediaItem *aItem, sbExtensionToContentFormatEntry_t &aFormatType, PRUint32 &aBitRate, PRUint32 &aSampleRate)
For a media item, get format information describing it (extension, mime type, etc.
Definition: sbDeviceUtils.h:54
virtual nsresult SupportsMediaItemDRM(sbIMediaItem *aMediaItem, PRBool aReportErrors, PRBool *_retval)
GstMessage * message
sbITranscodeProfile * transcodeProfile
Definition: sbBaseDevice.h:158
static sbTranscodeProgressListener * New(sbBaseDevice *aDeviceBase, sbDeviceStatusHelper *aDeviceStatusHelper, sbIMediaItem *aItem, PRMonitor *aCompleteNotifyMonitor=nsnull, StatusProperty const &aStatusProperty=StatusProperty(), sbIJobCancelable *aCancel=nsnull)
StringArrayEnumerator prototype hasMore
static nsresult DoesItemNeedTranscoding(sbExtensionToContentFormatEntry_t &aFormatType, PRUint32 &aBitRate, PRUint32 &aSampleRate, sbIDevice *aDevice, bool &aNeedsTranscoding)
Determine if an item needs transcoding.
static nsresult DispatchTranscodeError(sbITranscodeError *aError, sbBaseDevice *aDevice)
Songbird Device Status Services Definitions.
nsresult GetSupportedAlbumArtFormats(nsIArray **aFormats)
#define SB_MEDIAINSPECTOR_CONTRACTID
#define SB_MEDIAFORMAT_CONTRACTID
virtual nsresult GetSupportedTranscodeProfiles(PRUint32 aType, nsIArray **aSupportedProfiles)
nsCOMPtr< sbIMediaItem > item
Definition: sbBaseDevice.h:155
Interface that defines a single item of media in the system.
#define SB_PROPERTY_CONTENTURL
nsCOMPtr< sbITranscodeAlbumArt > albumArt
Definition: sbBaseDevice.h:183
#define SB_MEDIAFORMATAUDIO_CONTRACTID
const_iterator begin() const
static nsresult GetTranscodingConfigurator(PRUint32 aTranscodeType, sbIDeviceTranscodingConfigurator **aConfigurator)
static PRUint32 GetTranscodeType(sbIMediaItem *aMediaItem)
#define TRACE(args)
nsresult SelectTranscodeProfile(PRUint32 aTranscodeType, sbITranscodeProfile **aProfile)
Select a transcode profile to use when transcoding to this device.
The manager from which to request a transcoding job, transcoding profiles, etc.
var file
char const * ContainerFormat
Definition: sbDeviceUtils.h:57
virtual nsresult GetSupportedTranscodeProfiles(PRUint32 aType, nsIArray **aSupportedProfiles)