sbGStreamerTranscodeDeviceConfigurator.cpp
Go to the documentation of this file.
1 /* vim: set sw=2 : */
2 /*
3  *=BEGIN SONGBIRD GPL
4  *
5  * This file is part of the Songbird web player.
6  *
7  * Copyright(c) 2005-2009 POTI, Inc.
8  * http://www.songbirdnest.com
9  *
10  * This file may be licensed under the terms of of the
11  * GNU General Public License Version 2 (the ``GPL'').
12  *
13  * Software distributed under the License is distributed
14  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
15  * express or implied. See the GPL for the specific language
16  * governing rights and limitations.
17  *
18  * You should have received a copy of the GPL along with this
19  * program. If not, go to http://www.gnu.org/licenses/gpl.html
20  * or write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *=END SONGBIRD GPL
24  */
25 
26 #if defined(XP_WIN)
27 // needed before math.h to import HUGE_VAL correctly
28 #define _DLL
29 #endif /* XP_WIN */
30 
33 
35 #include <nsIArray.h>
36 #include <nsIFile.h>
37 #include <nsIFileURL.h>
38 #include <nsIMutableArray.h>
39 #include <nsIURI.h>
40 #include <nsIVariant.h>
41 #include <nsIWritablePropertyBag.h>
42 #include <nsIWritablePropertyBag2.h>
43 
45 #include <sbIDevice.h>
46 #include <sbIDeviceCapabilities.h>
47 #include <sbIMediaFormatMutable.h>
48 #include <sbITranscodeError.h>
49 #include <sbITranscodeManager.h>
50 #include <sbITranscodeProfile.h>
51 
53 #include <nsArrayUtils.h>
54 #include <nsCOMArray.h>
55 #include <nsComponentManagerUtils.h>
56 #include <nsNetUtil.h>
57 #include <prlog.h>
58 
60 #include <sbArrayUtils.h>
61 #include <sbMemoryUtils.h>
62 #include <sbStringUtils.h>
63 #include <sbTranscodeUtils.h>
64 #include <sbVariantUtils.h>
65 
67 #if _MSC_VER
68 # pragma warning (push)
69 # pragma warning (disable: 4244)
70 #endif /* _MSC_VER */
71 #include <gst/gst.h>
72 #if _MSC_VER
73 # pragma warning (pop)
74 #endif /* _MSC_VER */
75 
77 #include <math.h>
78 #include <algorithm>
79 #include <functional>
80 #include <vector>
81 
84 
86 
90 #ifdef PR_LOGGING
91 static PRLogModuleInfo* gGstTranscodeConfiguratorLog = nsnull;
92 #define TRACE(args) PR_LOG(gGstTranscodeConfiguratorLog, PR_LOG_DEBUG, args)
93 #define LOG(args) PR_LOG(gGstTranscodeConfiguratorLog, PR_LOG_WARN, args)
94 #if __GNUC__
95 #define __FUNCTION__ __PRETTY_FUNCTION__
96 #endif /* __GNUC__ */
97 #else
98 #define TRACE(args) /* nothing */
99 #define LOG(args) /* nothing */
100 #endif /* PR_LOGGING */
101 
103 
114 static nsresult
115 GetDevCapRangeUpper(sbIDevCapRange *aRange, PRInt32 aTarget, PRInt32 *_retval)
116 {
117  TRACE(("%s", __FUNCTION__));
118  NS_ENSURE_ARG_POINTER(aRange);
119  NS_ENSURE_ARG_POINTER(_retval);
120 
121  nsresult rv;
122  PRUint32 count;
123  PRInt32 result = PR_INT32_MIN, max = PR_INT32_MIN;
124  rv = aRange->GetValueCount(&count);
125  NS_ENSURE_SUCCESS(rv, rv);
126  if (count > 0) {
127  for (PRUint32 i = 0; i < count; ++i) {
128  PRInt32 v;
129  rv = aRange->GetValue(i, &v);
130  NS_ENSURE_SUCCESS(rv, rv);
131  if ((result < aTarget || v < result) && v >= aTarget) {
132  result = v;
133  }
134  else if (max < v) {
135  max = v;
136  }
137  }
138  if (result >= aTarget) {
139  *_retval = result;
140  return NS_OK;
141  }
142  else {
143  *_retval = max;
144  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
145  }
146  }
147 
148  // no count, use min + step + max
149  PRInt32 min, step;
150  rv = aRange->GetMin(&min);
151  rv |= aRange->GetStep(&step);
152  rv |= aRange->GetMax(&max);
153  NS_ENSURE_SUCCESS(rv, rv);
154  NS_ENSURE_TRUE(step > 0, NS_ERROR_UNEXPECTED);
155  if (max < aTarget) {
156  *_retval = max;
157  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
158  }
159  if (min > aTarget) {
160  *_retval = min;
161  return NS_OK;
162  }
163  // _retval = min + ceil((target - min) / step) * step
164  *_retval = min + (aTarget - min + step - 1) / step * step;
165  return NS_OK;
166 
167 }
168 
174 
176  : mQuality(-HUGE_VAL),
177  mVideoBitrate(PR_INT32_MIN)
178 {
179  #if PR_LOGGING
180  if (!gGstTranscodeConfiguratorLog) {
181  gGstTranscodeConfiguratorLog =
182  PR_NewLogModule("sbGStreamerTranscodeDeviceConfigurator");
183  }
184  #endif /* PR_LOGGING */
185  TRACE(("%s[%p]", __FUNCTION__, this));
186  /* nothing */
187 }
188 
190 {
191  /* nothing */
192 }
193 
201 nsresult
203  const nsACString& aMimeType,
204  nsIArray *aAttributes,
205  GstCaps** aResultCaps)
206 {
207  TRACE(("%s", __FUNCTION__));
208  NS_ENSURE_ARG_POINTER(aAttributes);
209  NS_ENSURE_ARG_POINTER(aResultCaps);
210 
211  nsresult rv;
212 
213  nsCOMPtr<nsISimpleEnumerator> attrsEnum;
214  rv = aAttributes->Enumerate(getter_AddRefs(attrsEnum));
215  NS_ENSURE_SUCCESS(rv, rv);
216 
217  // set up a caps structure
218  sbGstCaps caps = GetCapsForMimeType (aMimeType, aType);
219  GstStructure* capsStruct = gst_caps_get_structure(caps.get(), 0);
220 
221  PRBool hasMore;
222  while (NS_SUCCEEDED(rv = attrsEnum->HasMoreElements(&hasMore)) && hasMore) {
223  nsCOMPtr<nsISupports> attrSupports;
224  rv = attrsEnum->GetNext(getter_AddRefs(attrSupports));
225  NS_ENSURE_SUCCESS(rv, rv);
226  nsCOMPtr<sbITranscodeProfileAttribute> attr =
227  do_QueryInterface(attrSupports, &rv);
228  NS_ENSURE_SUCCESS(rv, rv);
229  nsString attrName;
230  rv = attr->GetName(attrName);
231  NS_ENSURE_SUCCESS(rv, rv);
232  nsCOMPtr<nsIVariant> attrValue;
233  rv = attr->GetValue(getter_AddRefs(attrValue));
234  NS_ENSURE_SUCCESS(rv, rv);
235  PRUint16 attrType;
236  rv = attrValue->GetDataType(&attrType);
237  NS_ENSURE_SUCCESS(rv, rv);
238  switch(attrType) {
239  case nsIDataType::VTYPE_INT8: case nsIDataType::VTYPE_UINT8:
240  case nsIDataType::VTYPE_INT16: case nsIDataType::VTYPE_UINT16:
241  case nsIDataType::VTYPE_INT32: case nsIDataType::VTYPE_UINT32:
242  case nsIDataType::VTYPE_INT64: case nsIDataType::VTYPE_UINT64:
243  {
244  PRInt32 intValue;
245  rv = attrValue->GetAsInt32(&intValue);
246  NS_ENSURE_SUCCESS(rv, rv);
247  gst_structure_set(capsStruct,
248  NS_LossyConvertUTF16toASCII(attrName).get(),
249  G_TYPE_INT,
250  intValue,
251  NULL);
252  break;
253  }
254  case nsIDataType::VTYPE_UTF8STRING:
255  case nsIDataType::VTYPE_DOMSTRING:
256  case nsIDataType::VTYPE_CSTRING:
257  case nsIDataType::VTYPE_ASTRING:
258  {
259  nsCString stringValue;
260  rv = attrValue->GetAsACString(stringValue);
261  NS_ENSURE_SUCCESS (rv, rv);
262 
263  gst_structure_set(capsStruct,
264  NS_LossyConvertUTF16toASCII(attrName).get(),
265  G_TYPE_STRING,
266  stringValue.BeginReading(),
267  NULL);
268  break;
269  }
270  default:
271  NS_NOTYETIMPLEMENTED("Unknown attribute type");
272  }
273  }
274  NS_ENSURE_SUCCESS(rv, rv);
275 
276  *aResultCaps = caps.forget();
277  return NS_OK;
278 }
279 
280 nsresult
282 {
283  TRACE(("%s[%p]", __FUNCTION__, this));
284  NS_ENSURE_ARG_POINTER(aProfile);
285 
286  nsresult rv;
287 
288  // for now, only support video profiles
289  PRUint32 type;
290  rv = aProfile->GetType(&type);
291  NS_ENSURE_SUCCESS(rv, rv);
292  switch(type) {
294  break;
295  default:
296  return NS_ERROR_NOT_IMPLEMENTED;
297  }
298 
299  EncoderProfileData elementNames;
300 
301  // check that we have a muxer available
302  nsString capsName;
303  rv = aProfile->GetContainerFormat(capsName);
304  NS_ENSURE_SUCCESS(rv, rv);
305  if (!capsName.IsEmpty()) {
306  nsCOMPtr<nsIArray> attrs;
307  rv = aProfile->GetContainerAttributes(getter_AddRefs(attrs));
308  NS_ENSURE_SUCCESS(rv, rv);
309 
310  GstCaps* caps = NULL;
312  NS_LossyConvertUTF16toASCII(capsName),
313  attrs,
314  &caps);
315  NS_ENSURE_SUCCESS(rv, rv);
316 
317  const char* muxerCodecName = FindMatchingElementName(caps, "Muxer");
318  if (!muxerCodecName) {
319  // things like id3 are called Formatter instead, but are the same for
320  // our purposes
321  muxerCodecName = FindMatchingElementName(caps, "Formatter");
322  }
323  gst_caps_unref(caps);
324  if (!muxerCodecName) {
325  TRACE(("%s: no muxer available for %s",
326  __FUNCTION__,
327  NS_LossyConvertUTF16toASCII(capsName).get()));
328  return NS_ERROR_UNEXPECTED;
329  }
330  elementNames.muxer = muxerCodecName;
331  }
332 
334  rv = aProfile->GetAudioCodec(capsName);
335  NS_ENSURE_SUCCESS(rv, rv);
336  if (!capsName.IsEmpty()) {
337  nsCOMPtr<nsIArray> attrs;
338  rv = aProfile->GetAudioAttributes(getter_AddRefs(attrs));
339  NS_ENSURE_SUCCESS(rv, rv);
340 
341  GstCaps* caps = NULL;
343  NS_LossyConvertUTF16toASCII(capsName),
344  attrs,
345  &caps);
346  NS_ENSURE_SUCCESS(rv, rv);
347 
348  const char* audioEncoder = FindMatchingElementName(caps, "Encoder");
349  gst_caps_unref(caps);
350  if (!audioEncoder) {
351  TRACE(("%s: no audio encoder available for %s",
352  __FUNCTION__,
353  NS_LossyConvertUTF16toASCII(capsName).get()));
354  return NS_ERROR_UNEXPECTED;
355  }
356  elementNames.audioEncoder = audioEncoder;
357  }
358 
360  rv = aProfile->GetVideoCodec(capsName);
361  NS_ENSURE_SUCCESS(rv, rv);
362  if (!capsName.IsEmpty()) {
363  nsCOMPtr<nsIArray> attrs;
364  rv = aProfile->GetVideoAttributes(getter_AddRefs(attrs));
365  NS_ENSURE_SUCCESS(rv, rv);
366 
367  GstCaps* caps = NULL;
369  NS_LossyConvertUTF16toASCII(capsName),
370  attrs,
371  &caps);
372  NS_ENSURE_SUCCESS(rv, rv);
373 
374  const char* videoEncoder = FindMatchingElementName(caps, "Encoder");
375  gst_caps_unref(caps);
376  if (!videoEncoder) {
377  TRACE(("%s: no video encoder available for %s",
378  __FUNCTION__,
379  NS_LossyConvertUTF16toASCII(capsName).get()));
380  return NS_ERROR_UNEXPECTED;
381  }
382  elementNames.videoEncoder = videoEncoder;
383  }
384 
385  PRBool success = mElementNames.Put(aProfile, elementNames);
386  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
387 
388  return NS_OK;
389 }
390 
391 nsresult
393 {
394  nsresult rv;
395  if (mQuality != -HUGE_VAL) {
396  // already set
397  return NS_OK;
398  }
399  if (!mDevice) {
400  // we don't have a device to read things from :(
401  // default to 0.5
402  rv = SetQuality(0.5);
403  NS_ENSURE_SUCCESS(rv, rv);
404  return NS_OK;
405  }
406 
407  double quality = 0.5;
408  nsCOMPtr<nsIVariant> qualityVar;
409  rv = mDevice->GetPreference(NS_LITERAL_STRING("transcode.quality.video"),
410  getter_AddRefs(qualityVar));
411  if (NS_FAILED(rv)) {
412  rv = SetQuality(quality);
413  NS_ENSURE_SUCCESS(rv, rv);
414  return NS_OK;
415  }
416  PRUint16 variantType;
417  rv = qualityVar->GetDataType(&variantType);
418  NS_ENSURE_SUCCESS(rv, rv);
419  switch (variantType) {
420  case nsIDataType::VTYPE_EMPTY:
421  case nsIDataType::VTYPE_VOID:
422  break;
423  default:
424  rv = qualityVar->GetAsDouble(&quality);
425  NS_ENSURE_SUCCESS(rv, rv);
426  break;
427  }
428  rv = SetQuality(quality);
429  NS_ENSURE_SUCCESS(rv, rv);
430  TRACE(("%s: set quality to %f", __FUNCTION__, quality));
431  return NS_OK;
432 }
433 
442 nsresult
444 {
445  TRACE(("%s[%p]", __FUNCTION__, this));
451  NS_PRECONDITION(mDevice, "SelectProfile called with no device set!");
452  NS_PRECONDITION(mQuality != -HUGE_VAL, "quality is not set!");
453 
454  nsresult rv;
455 
456  // the best compatible encoder profile
457  nsCOMPtr<sbITranscodeEncoderProfile> selectedProfile;
458  // the priority we got for that profile
459  PRUint32 selectedPriority = 0;
460  // the device format for the selected profile
461  nsCOMPtr<sbIVideoFormatType> selectedFormat;
462 
463  // get available encoder profiles
464  nsCOMPtr<nsIArray> profilesArray;
465  rv = GetAvailableProfiles(getter_AddRefs(profilesArray));
466  NS_ENSURE_SUCCESS(rv, rv);
467  nsCOMPtr<nsISimpleEnumerator> profilesEnum;
468  rv = profilesArray->Enumerate(getter_AddRefs(profilesEnum));
469  NS_ENSURE_SUCCESS(rv, rv);
470 
471  // get the device caps
472  nsCOMPtr<sbIDeviceCapabilities> caps;
473  rv = mDevice->GetCapabilities(getter_AddRefs(caps));
474  NS_ENSURE_SUCCESS(rv, rv);
475 
476  // XXXMook: video only for now
477  PRUint32 mimeTypesCount;
478  char **mimeTypes;
479  rv = caps->GetSupportedMimeTypes(sbIDeviceCapabilities::CONTENT_VIDEO,
480  &mimeTypesCount,
481  &mimeTypes);
482  if (NS_FAILED(rv)) {
483  // report an error
484  nsresult rv2;
485  nsString deviceName;
486  rv2 = mDevice->GetName(deviceName);
487  if (NS_FAILED(rv2)) {
488  deviceName =
489  SBLocalizedString("transcode.error.device_no_video.unknown_device");
490  }
491  nsTArray<nsString> detailParams;
492  detailParams.AppendElement(deviceName);
493  nsString detail =
494  SBLocalizedString("transcode.error.device_no_video.details", detailParams);
495  rv2 = SB_NewTranscodeError(NS_LITERAL_STRING("transcode.error.device_no_video.hasitem"),
496  NS_LITERAL_STRING("transcode.error.device_no_video.withoutitem"),
497  detail,
498  mInputUri,
499  nsnull,
500  getter_AddRefs(mLastError));
501  NS_ENSURE_SUCCESS(rv2, rv2);
502  }
503  NS_ENSURE_SUCCESS(rv, rv);
504 
505  sbAutoFreeXPCOMArrayByRef<char**> autoMimeTypes(mimeTypesCount, mimeTypes);
506 
507  PRBool hasMoreProfiles;
508  while (NS_SUCCEEDED(profilesEnum->HasMoreElements(&hasMoreProfiles)) &&
509  hasMoreProfiles)
510  {
511  nsCOMPtr<nsISupports> profileSupports;
512  rv = profilesEnum->GetNext(getter_AddRefs(profileSupports));
513  NS_ENSURE_SUCCESS(rv, rv);
514 
515  nsCOMPtr<sbITranscodeEncoderProfile> profile =
516  do_QueryInterface(profileSupports, &rv);
517  NS_ENSURE_SUCCESS(rv, rv);
518 
519  // check the priority first, that's easier
520  PRUint32 priority;
521  rv = profile->GetEncoderProfilePriority(mQuality, &priority);
522  NS_ENSURE_SUCCESS(rv, rv);
523  if (priority <= selectedPriority) {
524  continue;
525  }
526 
527  for (PRUint32 mimeTypeIndex = 0;
528  mimeTypeIndex < mimeTypesCount;
529  ++mimeTypeIndex)
530  {
531  /* We get the preferred format types here - these are the ones that it's
532  ok to transcode to (rather than the full set of things supported by
533  the device) */
534  nsISupports** formatTypes;
535  PRUint32 formatTypeCount;
536  rv = caps->GetPreferredFormatTypes(
538  NS_ConvertASCIItoUTF16(mimeTypes[mimeTypeIndex]),
539  &formatTypeCount,
540  &formatTypes);
541  NS_ENSURE_SUCCESS(rv, rv);
542  sbAutoFreeXPCOMPointerArray<nsISupports> freeFormats(formatTypeCount,
543  formatTypes);
544 
545  for (PRUint32 formatIndex = 0;
546  formatIndex < formatTypeCount;
547  formatIndex++)
548  {
549  nsCOMPtr<sbIVideoFormatType> format = do_QueryInterface(
550  formatTypes[formatIndex], &rv);
551  if (NS_FAILED(rv) || !format) {
552  // XXX Mook: we only support video for now
553  continue;
554  }
555 
556  // check the container
557  nsCString formatContainer;
558  rv = format->GetContainerType(formatContainer);
559  NS_ENSURE_SUCCESS(rv, rv);
560  nsString encoderContainer;
561  rv = profile->GetContainerFormat(encoderContainer);
562  NS_ENSURE_SUCCESS(rv, rv);
563 
564  if (!encoderContainer.Equals(NS_ConvertUTF8toUTF16(formatContainer))) {
565  // mismatch, try the next device format
566  continue;
567  }
568 
569  // check the audio codec
570  nsString encoderAudioCodec;
571  rv = profile->GetAudioCodec(encoderAudioCodec);
572  NS_ENSURE_SUCCESS(rv, rv);
573  nsCOMPtr<sbIDevCapAudioStream> audioCaps;
574  rv = format->GetAudioStream(getter_AddRefs(audioCaps));
575  NS_ENSURE_SUCCESS(rv, rv);
576  if (!audioCaps) {
577  if (!encoderAudioCodec.IsEmpty()) {
578  // this device format doesn't support audio, but the encoder does
579  // skip for now, I think?
580  continue;
581  }
582  }
583  else {
584  nsCString formatAudioCodec;
585  rv = audioCaps->GetType(formatAudioCodec);
586  NS_ENSURE_SUCCESS(rv, rv);
587  if (!encoderAudioCodec.Equals(NS_ConvertUTF8toUTF16(formatAudioCodec))) {
588  // mismatch, try the next device format
589  continue;
590  }
591  // XXX Mook: TODO: match properties
592  }
593 
594  // check the video codec
595  nsString encoderVideoCodec;
596  rv = profile->GetVideoCodec(encoderVideoCodec);
597  NS_ENSURE_SUCCESS(rv, rv);
598  nsCOMPtr<sbIDevCapVideoStream> videoCaps;
599  rv = format->GetVideoStream(getter_AddRefs(videoCaps));
600  NS_ENSURE_SUCCESS(rv, rv);
601  if (!videoCaps) {
602  if (!encoderVideoCodec.IsEmpty()) {
603  // this device format doesn't support video, but the encoder does
604  // skip for now, I think?
605  continue;
606  }
607  }
608  else {
609  nsCString formatVideoCodec;
610  rv = videoCaps->GetType(formatVideoCodec);
611  NS_ENSURE_SUCCESS(rv, rv);
612  if (!encoderVideoCodec.Equals(NS_ConvertUTF8toUTF16(formatVideoCodec))) {
613  // mismatch, try the next device format
614  continue;
615  }
616  // XXX Mook: TODO: match properties
617  }
618 
619  // assume we match here
620  selectedProfile = profile;
621  selectedFormat = format;
622  rv = profile->GetEncoderProfilePriority(mQuality, &selectedPriority);
623  NS_ENSURE_SUCCESS(rv, rv);
624  }
625  }
626  // if we reach here, we have either added it to the available list or
627  // the encoder profile is not compatible; either way, nothing to do here
628  }
629 
630  mSelectedProfile = selectedProfile;
631  if (!mSelectedProfile) {
632  // no suitable encoder profile found
633  TRACE(("%s: no suitable encoder profile found", __FUNCTION__));
634  // report an error
635  nsString deviceName;
636  rv = mDevice->GetName(deviceName);
637  if (NS_FAILED(rv)) {
638  deviceName =
639  SBLocalizedString("transcode.error.device_no_video.unknown_device");
640  }
641  nsTArray<nsString> detailParams;
642  detailParams.AppendElement(deviceName);
643  nsString detail =
644  SBLocalizedString("transcode.error.no_profile.details", detailParams);
645  rv = SB_NewTranscodeError(NS_LITERAL_STRING("transcode.error.no_profile.hasitem"),
646  NS_LITERAL_STRING("transcode.error.no_profile.withoutitem"),
647  detail,
648  mInputUri,
649  nsnull,
650  getter_AddRefs(mLastError));
651  NS_ENSURE_SUCCESS(rv, rv);
652  return NS_ERROR_FAILURE;
653  }
654  mSelectedFormat = selectedFormat;
655 
656  EncoderProfileData elementNames;
657  PRBool success = mElementNames.Get(selectedProfile, &elementNames);
658  NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
659  CopyASCIItoUTF16(elementNames.muxer, mMuxer);
660  CopyASCIItoUTF16(elementNames.audioEncoder, mAudioEncoder);
661  CopyASCIItoUTF16(elementNames.videoEncoder, mVideoEncoder);
662  rv = selectedProfile->GetFileExtension(mFileExtension);
663  NS_ENSURE_SUCCESS(rv, rv);
664 
665  TRACE(("%s: profile selected, using muxer [%s] audio [%s] video [%s] extension [%s]",
666  __FUNCTION__,
667  elementNames.muxer.get(),
668  elementNames.audioEncoder.get(),
669  elementNames.videoEncoder.get(),
670  mFileExtension.get()));
671 
672  /* Set whether we're using these - in this configurator, this is based
673  entirely on whether we've selected a specific element */
674  if (!mMuxer.IsEmpty())
675  mUseMuxer = PR_TRUE;
676  if (!mAudioEncoder.IsEmpty())
677  mUseAudioEncoder = PR_TRUE;
678  if (!mVideoEncoder.IsEmpty())
679  mUseVideoEncoder = PR_TRUE;
680 
681  return NS_OK;
682 }
683 
690 nsresult
692 {
693  TRACE(("%s[%p]", __FUNCTION__, this));
694  NS_PRECONDITION(mSelectedProfile,
695  "attempted to set audio properties without selecting profile");
696  NS_PRECONDITION(mSelectedFormat,
697  "attempted to set audio properties without selected output format");
698 
699  nsresult rv;
700 
701  if (!mAudioFormat) {
702  mAudioFormat = do_CreateInstance(SB_MEDIAFORMATAUDIO_CONTRACTID, &rv);
703  NS_ENSURE_SUCCESS(rv, rv);
704  }
705 
706  nsCOMPtr<sbIMediaFormatAudioMutable> audioFormat =
707  do_QueryInterface(mAudioFormat, &rv);
708  NS_ENSURE_SUCCESS(rv, rv);
709 
710  nsCOMPtr<sbIMediaFormatAudio> inputFormat;
711  rv = mInputFormat->GetAudioStream(getter_AddRefs(inputFormat));
712  NS_ENSURE_SUCCESS(rv, rv);
713  if (inputFormat) {
714  // Get the device output audio info
715  nsCOMPtr<sbIDevCapAudioStream> outputCaps;
716  rv = mSelectedFormat->GetAudioStream(getter_AddRefs(outputCaps));
717  NS_ENSURE_SUCCESS(rv, rv);
718 
719  PRBool isInRange;
720 
721  nsCOMPtr<sbIDevCapRange> sampleRateRange;
722  rv = outputCaps->GetSupportedSampleRates(getter_AddRefs(sampleRateRange));
723  NS_ENSURE_SUCCESS(rv, rv);
724  PRInt32 sampleRate;
725  rv = inputFormat->GetSampleRate(&sampleRate);
726  NS_ENSURE_SUCCESS(rv, rv);
727  rv = sampleRateRange->IsValueInRange(sampleRate, &isInRange);
728  if (NS_FAILED(rv) || !isInRange) {
729  // won't fit; pick anything for now
730  rv = GetDevCapRangeUpper(sampleRateRange, sampleRate, &sampleRate);
731  NS_ENSURE_SUCCESS(rv, rv);
732  }
733  rv = audioFormat->SetSampleRate(sampleRate);
734  NS_ENSURE_SUCCESS(rv, rv);
735 
736  nsCOMPtr<sbIDevCapRange> channelsRange;
737  rv = outputCaps->GetSupportedChannels(getter_AddRefs(channelsRange));
738  NS_ENSURE_SUCCESS(rv, rv);
739  PRInt32 channels;
740  rv = inputFormat->GetChannels(&channels);
741  NS_ENSURE_SUCCESS(rv, rv);
742  rv = channelsRange->IsValueInRange(channels, &isInRange);
743  if (NS_FAILED(rv) || !isInRange) {
744  // won't fit; pick anything for now
745  PRInt32 newChannels;
746  rv = GetDevCapRangeUpper(channelsRange, channels, &newChannels);
747  if (NS_SUCCEEDED(rv)) {
748  channels = newChannels;
749  }
750  else {
751  // no channel information; assume supports mono + stereo
752  channels = (channels < 2 ? 1 : 2);
753  }
754  }
755  rv = audioFormat->SetChannels(channels);
756  NS_ENSURE_SUCCESS(rv, rv);
757  }
758  else {
759  // no audio stream
760  mAudioEncoder.SetIsVoid(PR_TRUE);
761  }
764  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
765  NS_ENSURE_SUCCESS(rv, rv);
766  }
767 
768  nsCOMPtr<nsIWritablePropertyBag> writableBag =
769  do_QueryInterface(mAudioEncoderProperties, &rv);
770  NS_ENSURE_SUCCESS(rv, rv);
771 
772  nsCOMPtr<nsIArray> propsSrc;
773  rv = mSelectedProfile->GetAudioProperties(getter_AddRefs(propsSrc));
774  NS_ENSURE_SUCCESS(rv, rv);
775 
776  rv = CopyPropertiesIntoBag(propsSrc, writableBag, PR_FALSE);
777  NS_ENSURE_SUCCESS(rv, rv);
778 
779  return NS_OK;
780 }
781 
788 nsresult
790 {
791  TRACE(("%s[%p]", __FUNCTION__, this));
792  NS_PRECONDITION(mSelectedProfile,
793  "DetermineIdealOutputSize called without selected profile");
794  NS_PRECONDITION(mSelectedFormat,
795  "DetermineIdealOutputSize called without selected format");
796  NS_PRECONDITION(mInputFormat,
797  "DetermineIdealOutputSize called without input format");
798 
799  nsresult rv;
800 
801  // Get the desired output frame rate
802  sbFraction outputFrameRate(PR_UINT32_MAX, 1);
803  // get the desired frame rate
804  { /* scope */
805  nsCOMPtr<sbIMediaFormatVideo> inputFormat;
806  rv = mInputFormat->GetVideoStream(getter_AddRefs(inputFormat));
807  NS_ENSURE_SUCCESS(rv, rv);
808  NS_ENSURE_TRUE(inputFormat, NS_ERROR_FAILURE);
809  PRUint32 num, denom;
810  rv = inputFormat->GetVideoFrameRate(&num, &denom);
811  NS_ENSURE_SUCCESS(rv, rv);
812  sbFraction inputFrameRate(num, denom);
813  nsCOMPtr<sbIDevCapVideoStream> videoCaps;
814  rv = mSelectedFormat->GetVideoStream(getter_AddRefs(videoCaps));
815  NS_ENSURE_SUCCESS(rv, rv);
816  NS_ENSURE_TRUE(videoCaps, NS_ERROR_FAILURE);
817 
818  PRBool isFrameRatesRange;
819  rv = videoCaps->GetDoesSupportFrameRateRange(&isFrameRatesRange);
820  NS_ENSURE_SUCCESS(rv, rv);
821 
822  if (isFrameRatesRange) {
823  nsCOMPtr<sbIDevCapFraction> minFrameRate;
824  rv = videoCaps->GetMinimumSupportedFrameRate(
825  getter_AddRefs(minFrameRate));
826  NS_ENSURE_SUCCESS(rv, rv);
827  rv = minFrameRate->GetNumerator(&num);
828  NS_ENSURE_SUCCESS(rv, rv);
829  rv = minFrameRate->GetDenominator(&denom);
830  NS_ENSURE_SUCCESS(rv, rv);
831  sbFraction minFrameRateFraction(num, denom);
832 
833  nsCOMPtr<sbIDevCapFraction> maxFrameRate;
834  rv = videoCaps->GetMaximumSupportedFrameRate(
835  getter_AddRefs(maxFrameRate));
836  NS_ENSURE_SUCCESS(rv, rv);
837  rv = maxFrameRate->GetNumerator(&num);
838  NS_ENSURE_SUCCESS(rv, rv);
839  rv = maxFrameRate->GetDenominator(&denom);
840  NS_ENSURE_SUCCESS(rv, rv);
841  sbFraction maxFrameRateFraction(num, denom);
842 
843  if (inputFrameRate >= minFrameRateFraction &&
844  inputFrameRate <= maxFrameRateFraction)
845  {
846  outputFrameRate = inputFrameRate;
847  }
848  else if (inputFrameRate < minFrameRateFraction) {
849  outputFrameRate = minFrameRateFraction;
850  }
851  else if (inputFrameRate > maxFrameRateFraction) {
852  outputFrameRate = maxFrameRateFraction;
853  }
854  }
855  else {
856  nsCOMPtr<nsIArray> frameRatesRange;
857  rv = videoCaps->GetSupportedFrameRates(getter_AddRefs(frameRatesRange));
858  NS_ENSURE_SUCCESS(rv, rv);
859 
860  PRUint32 length = 0;
861  rv = frameRatesRange->GetLength(&length);
862  NS_ENSURE_SUCCESS(rv, rv);
863 
864  for (PRUint32 i = 0; i < length; i++) {
865  nsCOMPtr<sbIDevCapFraction> curFraction =
866  do_QueryElementAt(frameRatesRange, i, &rv);
867  NS_ENSURE_SUCCESS(rv, rv);
868 
869  PRUint32 num, denom;
870  rv = curFraction->GetNumerator(&num);
871  NS_ENSURE_SUCCESS(rv, rv);
872  rv = curFraction->GetDenominator(&denom);
873  NS_ENSURE_SUCCESS(rv, rv);
874 
875  sbFraction candidate(num, denom);
876 
877  double lastDifference = fabs(outputFrameRate - inputFrameRate);
878  double difference = fabs(candidate - inputFrameRate);
879  if (difference < lastDifference) {
880  outputFrameRate = candidate;
881  }
882  }
883  }
884 
885  mVideoFrameRate = outputFrameRate;
886  }
887 
888  /*
889  * find the smallest output format that has at least many pixels (horizontally
890  * as well as vertically) as the input format
891  */
892 
893  nsCOMPtr<sbIMediaFormatVideo> inputFormat;
894  rv = mInputFormat->GetVideoStream(getter_AddRefs(inputFormat));
895  NS_ENSURE_SUCCESS(rv, rv);
896  if (!inputFormat) {
897  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
898  }
899  Dimensions input;
900  rv = inputFormat->GetVideoWidth(&input.width);
901  NS_ENSURE_SUCCESS(rv, rv);
902  rv = inputFormat->GetVideoHeight(&input.height);
903  NS_ENSURE_SUCCESS(rv, rv);
904 
905  nsCOMPtr<sbIDevCapVideoStream> outputCaps;
906  rv = mSelectedFormat->GetVideoStream(getter_AddRefs(outputCaps));
907  NS_ENSURE_SUCCESS(rv, rv);
908  NS_ENSURE_TRUE(outputCaps, NS_ERROR_FAILURE);
909 
910  // If we can't match the PAR, adjust to square pixels
911  { /* scope */
912  PRUint32 num, denom;
913  rv = inputFormat->GetVideoPAR(&num, &denom);
914  NS_ENSURE_SUCCESS(rv, rv);
915  sbFraction inputPAR(num, denom);
916 
917  // Check to see if the PAR values for the device caps is a min/max or a
918  // set range of values.
919  PRBool supportsPARRange;
920  rv = outputCaps->GetDoesSupportPARRange(&supportsPARRange);
921  NS_ENSURE_SUCCESS(rv, rv);
922 
923  if (supportsPARRange) {
924  // The PAR value is a min/max value range. Pick the closet match.
925  nsCOMPtr<sbIDevCapFraction> minPARFraction;
926  rv = outputCaps->GetMinimumSupportedPAR(getter_AddRefs(minPARFraction));
927  NS_ENSURE_SUCCESS(rv, rv);
928  rv = minPARFraction->GetNumerator(&num);
929  NS_ENSURE_SUCCESS(rv, rv);
930  rv = minPARFraction->GetDenominator(&denom);
931  NS_ENSURE_SUCCESS(rv, rv);
932  sbFraction minPAR(num, denom);
933 
934  nsCOMPtr<sbIDevCapFraction> maxPARFraction;
935  rv = outputCaps->GetMaximumSupportedPAR(getter_AddRefs(maxPARFraction));
936  NS_ENSURE_SUCCESS(rv, rv);
937  rv = maxPARFraction->GetNumerator(&num);
938  NS_ENSURE_SUCCESS(rv, rv);
939  rv = maxPARFraction->GetDenominator(&denom);
940  NS_ENSURE_SUCCESS(rv, rv);
941  sbFraction maxPAR(num, denom);
942 
943  // If the input PAR is between the min and max PAR values use the input
944  // PAR value. If not, use the closest PAR value.
945  if (inputPAR >= minPAR && inputPAR <= maxPAR) {
946  mOutputPAR = inputPAR;
947  }
948  else if (inputPAR < minPAR) {
949  mOutputPAR = minPAR;
950  }
951  else if (inputPAR > maxPAR) {
952  mOutputPAR = maxPAR;
953  }
954  }
955  else {
956  nsCOMPtr<nsIArray> parRanges;
957  rv = outputCaps->GetSupportedPARs(getter_AddRefs(parRanges));
958  NS_ENSURE_SUCCESS(rv, rv);
959  NS_ENSURE_TRUE(parRanges, NS_ERROR_UNEXPECTED);
960 
961  PRUint32 count, index;
962  rv = parRanges->GetLength(&count);
963  NS_ENSURE_SUCCESS(rv, rv);
964 
965  for (index = 0; index < count; index++) {
966  nsCOMPtr<sbIDevCapFraction> curFraction =
967  do_QueryElementAt(parRanges, index, &rv);
968  NS_ENSURE_SUCCESS(rv, rv);
969 
970  rv = curFraction->GetNumerator(&num);
971  NS_ENSURE_SUCCESS(rv, rv);
972  rv = curFraction->GetDenominator(&denom);
973  NS_ENSURE_SUCCESS(rv, rv);
974 
975  sbFraction curPARFraction(num, denom);
976  if (inputPAR.IsEqual(curPARFraction)) {
977  mOutputPAR = inputPAR;
978  break;
979  }
980  }
981 
982  if (index >= count) {
983  // we didn't find a match; just... kinda give up and blow them up into
984  // square-looking pixels by duplicating pixels
985  input.width *= inputPAR.Denominator();
986  input.height *= inputPAR.Numerator();
987  mOutputPAR = sbFraction(1, 1); // XXX Mook: we need to adjust to something
988  // we have output PAR for!
989  }
990  }
991  }
992 
993  { /* scope - try to use ranges */
994  PRBool hasRange = PR_TRUE;
995  std::vector<PRInt32> widths, heights;
996  Dimensions output(0, 0);
997  nsCOMPtr<sbIDevCapRange> range;
998  rv = outputCaps->GetSupportedWidths(getter_AddRefs(range));
999  NS_ENSURE_SUCCESS(rv, rv);
1000  if (range) {
1001  PRUint32 count;
1002  rv = range->GetValueCount(&count);
1003  NS_ENSURE_SUCCESS(rv, rv);
1004  if (count > 0) {
1005  for (PRUint32 i = 0; i < count; ++i) {
1006  PRInt32 v;
1007  rv = range->GetValue(i, &v);
1008  NS_ENSURE_SUCCESS(rv, rv);
1009  widths.push_back(v);
1010  }
1011  std::sort(widths.begin(), widths.end());
1012  }
1013  else {
1014  // no count, use min + step + max
1015  PRInt32 min, step, max;
1016  rv = range->GetMin(&min);
1017  rv |= range->GetStep(&step);
1018  rv |= range->GetMax(&max);
1019  NS_ENSURE_SUCCESS(rv, rv);
1020  for (PRInt32 v = min; v <= max; v += step) {
1021  widths.push_back(v);
1022  }
1023  }
1024  }
1025  else {
1026  hasRange = PR_FALSE;
1027  }
1028  if (hasRange) {
1029  rv = outputCaps->GetSupportedHeights(getter_AddRefs(range));
1030  NS_ENSURE_SUCCESS(rv, rv);
1031  if (range) {
1032  PRUint32 count;
1033  rv = range->GetValueCount(&count);
1034  NS_ENSURE_SUCCESS(rv, rv);
1035  if (count > 0) {
1036  for (PRUint32 i = 0; i < count; ++i) {
1037  PRInt32 v;
1038  rv = range->GetValue(i, &v);
1039  NS_ENSURE_SUCCESS(rv, rv);
1040  heights.push_back(v);
1041  }
1042  std::sort(heights.begin(), heights.end());
1043  }
1044  else {
1045  // no count, use min + step + max
1046  PRInt32 min, step, max;
1047  rv = range->GetMin(&min);
1048  rv |= range->GetStep(&step);
1049  rv |= range->GetMax(&max);
1050  NS_ENSURE_SUCCESS(rv, rv);
1051  for (PRInt32 v = min; v <= max; v += step) {
1052  heights.push_back(v);
1053  }
1054  }
1055  }
1056  else {
1057  hasRange = PR_FALSE;
1058  }
1059  }
1060  if (hasRange) {
1061  // we got a set of ranges, let's try to get some sort of output size
1062  output = input;
1063  // try to scale things up to the minimum size, preseving aspect ratio
1064  if (output.width < widths.front()) {
1065  output.width = widths.front();
1066  output.height = PRUint64(input.height) * widths.front() / input.width;
1067  }
1068  if (output.height < heights.front()) {
1069  output.height = heights.front();
1070  output.width = PRUint64(input.width) * heights.front() / input.height;
1071  }
1072  // cap to maximum size (this may affect aspect ratio)
1073  output = GetMaximumFit(output, Dimensions(widths.back(), heights.back()));
1074  // get it to a multiple of step size at least as big as the desired size
1075  // (this will also force it to at least minimum size if it has been
1076  // scaled down; nothing we can do about that, we'll just need black bars)
1077  std::vector<PRInt32>::const_iterator it;
1078  it = std::lower_bound(widths.begin(), widths.end(), output.width);
1079  if (it == widths.end()) {
1080  --it;
1081  }
1082  output.width = *it;
1083  it = std::lower_bound(heights.begin(), heights.end(), output.height);
1084  if (it == heights.end()) {
1085  --it;
1086  }
1087  output.height = *it;
1088 
1089  // we have something set via the ranges. no need to worry about the aspect
1090  // ratio, because the transcoder will deal with adding padding for us
1092  return NS_OK;
1093  }
1094  } /* end scope */
1095 
1096  // no ranges, we need to use explicit sizes
1097  // find the smallest explicit size that will fit
1098  Dimensions output(PR_INT32_MAX, PR_INT32_MAX);
1099  nsCOMPtr<nsIArray> explicitSizes;
1100  rv = outputCaps->GetSupportedExplicitSizes(getter_AddRefs(explicitSizes));
1101  NS_ENSURE_SUCCESS(rv, rv);
1102  nsCOMPtr<nsISimpleEnumerator> sizeEnum;
1103  rv = explicitSizes->Enumerate(getter_AddRefs(sizeEnum));
1104  NS_ENSURE_SUCCESS(rv, rv);
1105  PRBool hasMore;
1106  while (NS_SUCCEEDED(rv = sizeEnum->HasMoreElements(&hasMore)) && hasMore) {
1107  nsCOMPtr<nsISupports> supports;
1108  rv = sizeEnum->GetNext(getter_AddRefs(supports));
1109  NS_ENSURE_SUCCESS(rv, rv);
1110  nsCOMPtr<sbIImageSize> size = do_QueryInterface(supports, &rv);
1111  NS_ENSURE_SUCCESS(rv, rv);
1112  PRInt32 width, height;
1113  rv = size->GetWidth(&width);
1114  NS_ENSURE_SUCCESS(rv, rv);
1115  rv = size->GetHeight(&height);
1116  NS_ENSURE_SUCCESS(rv, rv);
1117  if (width < input.width || height < input.height) {
1118  // too small
1119  continue;
1120  }
1121  if (width > output.width && height > output.height) {
1122  // larger than what we already selected; not useful
1123  continue;
1124  }
1125  output.width = width;
1126  output.height = height;
1127  }
1128  NS_ENSURE_SUCCESS(rv, rv);
1129  if (output.width != PR_INT32_MAX || output.height != PR_INT32_MAX) {
1130  // found a size
1132  return NS_OK;
1133  }
1134 
1135  // the device has explicit supported sizes, but the source image is too big
1136  // to fit. Try to find the best fit.
1137  // As a simple algorithm, find the size the output will be when scaled down,
1138  // and use the one with the maximum width (since aspect ratio will be
1139  // preserved, and we assume square pixels, that is equivalent to the largest
1140  // number of pixels)
1141  rv = explicitSizes->Enumerate(getter_AddRefs(sizeEnum));
1142  NS_ENSURE_SUCCESS(rv, rv);
1143  Dimensions result, best(0, 0), selected(0, 0), maxSize;
1144  while (NS_SUCCEEDED(rv = sizeEnum->HasMoreElements(&hasMore)) && hasMore) {
1145  nsCOMPtr<nsISupports> supports;
1146  rv = sizeEnum->GetNext(getter_AddRefs(supports));
1147  NS_ENSURE_SUCCESS(rv, rv);
1148  nsCOMPtr<sbIImageSize> size = do_QueryInterface(supports);
1149  NS_ENSURE_SUCCESS(rv, rv);
1150  rv = size->GetWidth(&maxSize.width);
1151  NS_ENSURE_SUCCESS(rv, rv);
1152  rv = size->GetHeight(&maxSize.height);
1153  NS_ENSURE_SUCCESS(rv, rv);
1154  result = GetMaximumFit(input, maxSize);
1155  if (result.width > best.width) {
1156  best = result;
1157  selected = maxSize;
1158  }
1159  }
1160  NS_ENSURE_SUCCESS(rv, rv);
1161  if (best.width < 1) {
1162  // best width is still empty?
1163  return NS_ERROR_FAILURE;
1164  }
1165 
1166  mOutputDimensions = selected;
1167  return NS_OK;
1168 }
1169 
1174 {
1175  TRACE(("%s", __FUNCTION__));
1176  if (aInput.width <= aMaximum.width && aInput.height < aMaximum.height) {
1177  // things fit anyway! there was no need to call this.
1178  return aInput;
1179  }
1180  // At least one side exceeds the maximum rectangle; figure out which it is
1181  Dimensions result = aMaximum;
1182  if (PRUint64(aInput.width) * aMaximum.height > PRUint64(aInput.height) * aMaximum.width) {
1183  // the horzontal bounds will be hit first
1184  result.height = PRUint64(aInput.height) * aMaximum.width / aInput.width;
1185  }
1186  else {
1187  result.width = PRUint64(aInput.width) * aMaximum.height / aInput.height;
1188  }
1189  return result;
1190 }
1191 
1199 nsresult
1201 {
1202  TRACE(("%s[%p]", __FUNCTION__, this));
1203  NS_PRECONDITION(mOutputDimensions.width > 0 &&
1205  "FinalizeOutputSize needs dimensions");
1206  NS_PRECONDITION(mSelectedProfile,
1207  "FinalizeOutputSize called with no profile selected");
1208  NS_PRECONDITION(mSelectedFormat,
1209  "FinalizeOutputSize called with no output format");
1210  nsresult rv;
1211 
1212  // Calculate the maximum bitrate that makes sense - this is our "max bits per
1213  // pixel" value from the profile, multiplied by the number of pixels per
1214  // second.
1215  const double pixelsPerSecond = mVideoFrameRate *
1218  double q1BPP;
1219  rv = mSelectedProfile->GetVideoBitsPerPixel(1, &q1BPP);
1220  NS_ENSURE_SUCCESS(rv, rv);
1221  double maxBitrate = q1BPP * pixelsPerSecond;
1222  TRACE(("%s: max bitrate %f for bpp %f", __FUNCTION__, maxBitrate, q1BPP));
1223 
1224  // Now find a bitrate that is actually permitted for this device.
1225  nsCOMPtr<sbIDevCapVideoStream> videoCaps;
1226  rv = mSelectedFormat->GetVideoStream(getter_AddRefs(videoCaps));
1227  NS_ENSURE_SUCCESS(rv, rv);
1228  NS_ENSURE_TRUE(videoCaps, NS_ERROR_FAILURE);
1229 
1230  nsCOMPtr<sbIDevCapRange> videoBitrateRange;
1231  rv = videoCaps->GetSupportedBitRates(getter_AddRefs(videoBitrateRange));
1232  NS_ENSURE_SUCCESS(rv, rv);
1233  rv = GetDevCapRangeUpper(videoBitrateRange,
1234  static_cast<PRInt32>(maxBitrate),
1235  &mVideoBitrate);
1236  NS_ENSURE_SUCCESS(rv, rv);
1237 
1238  TRACE(("%s: selected bitrate %d", __FUNCTION__, mVideoBitrate));
1239 
1240  return NS_OK;
1241 }
1242 
1243 nsresult
1245 {
1246  NS_PRECONDITION(mOutputDimensions.width > 0 && mOutputDimensions.height > 0,
1247  "attempting to set video properties with no output dimensions!");
1248  NS_PRECONDITION(mVideoBitrate,
1249  "attempting to set video properties with no video bitrate!");
1250 
1251  nsresult rv;
1252  nsCOMPtr<sbIMediaFormatVideoMutable> videoFormat =
1253  do_CreateInstance(SB_MEDIAFORMATVIDEO_CONTRACTID, &rv);
1254  NS_ENSURE_SUCCESS(rv, rv);
1255 
1256  // set the video format data
1257  rv = videoFormat->SetVideoWidth(mOutputDimensions.width);
1258  NS_ENSURE_SUCCESS(rv, rv);
1259  rv = videoFormat->SetVideoHeight(mOutputDimensions.height);
1260  NS_ENSURE_SUCCESS(rv, rv);
1261  rv = videoFormat->SetVideoPAR(mOutputPAR.Numerator(),
1263  NS_ENSURE_SUCCESS(rv, rv);
1264  rv = videoFormat->SetVideoFrameRate(mVideoFrameRate.Numerator(),
1266  NS_ENSURE_SUCCESS(rv, rv);
1267  mVideoFormat = do_QueryInterface(videoFormat, &rv);
1268  NS_ENSURE_SUCCESS(rv, rv);
1269 
1270  if (!mVideoEncoderProperties) {
1272  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
1273  NS_ENSURE_SUCCESS(rv, rv);
1274  }
1275 
1276  // set arbitrary properties
1277  nsCOMPtr<nsIWritablePropertyBag> writableBag =
1278  do_QueryInterface(mVideoEncoderProperties, &rv);
1279  NS_ENSURE_SUCCESS(rv, rv);
1280 
1281  nsCOMPtr<nsIArray> propsSrc;
1282  rv = mSelectedProfile->GetVideoProperties(getter_AddRefs(propsSrc));
1283  NS_ENSURE_SUCCESS(rv, rv);
1284 
1285  rv = CopyPropertiesIntoBag(propsSrc, writableBag, PR_TRUE);
1286  NS_ENSURE_SUCCESS(rv, rv);
1287 
1288  NS_POSTCONDITION(mVideoFormat,
1289  "setVideoProperties failed to set video format");
1290  NS_POSTCONDITION(mVideoEncoderProperties,
1291  "setVideoProperties failed to set video properties");
1292  return NS_OK;
1293 }
1294 
1301 nsresult
1303  nsIWritablePropertyBag * aDstBag,
1304  PRBool aIsVideo)
1305 {
1306  NS_ENSURE_ARG_POINTER(aSrcProps);
1307  NS_ENSURE_ARG_POINTER(aDstBag);
1308 
1309  nsresult rv;
1310 
1311  nsCOMPtr<nsISimpleEnumerator> propsEnum;
1312  rv = aSrcProps->Enumerate(getter_AddRefs(propsEnum));
1313  NS_ENSURE_SUCCESS(rv, rv);
1314  PRBool hasMore;
1315  while (NS_SUCCEEDED(rv = propsEnum->HasMoreElements(&hasMore)) && hasMore) {
1316  nsCOMPtr<nsISupports> supports;
1317  rv = propsEnum->GetNext(getter_AddRefs(supports));
1318  NS_ENSURE_SUCCESS(rv, rv);
1319  nsCOMPtr<sbITranscodeProfileProperty> prop =
1320  do_QueryInterface(supports, &rv);
1321  NS_ENSURE_SUCCESS(rv, rv);
1322  PRBool hidden;
1323  rv = prop->GetHidden(&hidden);
1324  NS_ENSURE_SUCCESS(rv, rv);
1325  if (hidden) {
1326  continue;
1327  }
1328  nsString propName;
1329  rv = prop->GetPropertyName(propName);
1330  NS_ENSURE_SUCCESS(rv, rv);
1331 
1332  // get the value
1333  nsCOMPtr<nsIVariant> value;
1334  rv = prop->GetValue(getter_AddRefs(value));
1335  NS_ENSURE_SUCCESS(rv, rv);
1336  PRUint16 dataType;
1337  rv = value->GetDataType(&dataType);
1338  NS_ENSURE_SUCCESS(rv, rv);
1339 
1340  nsCString mapping;
1341  rv = prop->GetMapping(mapping);
1342  NS_ENSURE_SUCCESS(rv, rv);
1343  if (!mapping.IsEmpty()) {
1344  if (aIsVideo && mapping.Equals("bitrate", CaseInsensitiveCompare)) {
1345  value = sbNewVariant(mVideoBitrate);
1346  NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
1347  }
1348  else if (!aIsVideo && mapping.Equals("bitrate", CaseInsensitiveCompare)) {
1349  double audioBitrate;
1350  rv = mSelectedProfile->GetAudioBitrate(mQuality, &audioBitrate);
1351  NS_ENSURE_SUCCESS(rv, rv);
1352  value = sbNewVariant(audioBitrate);
1353  NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
1354  }
1355  else if (mapping.Equals("quality", CaseInsensitiveCompare)) {
1356  value = sbNewVariant(mQuality);
1357  NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
1358  }
1359  else if (mapping.Equals("video-quality", CaseInsensitiveCompare)) {
1360  value = sbNewVariant(mQuality);
1361  NS_ENSURE_TRUE(value, NS_ERROR_OUT_OF_MEMORY);
1362  }
1363  else {
1364  TRACE(("%s[%p]: mapping %s not implemented",
1365  __FUNCTION__, this, mapping.get()));
1366  return NS_ERROR_NOT_IMPLEMENTED;
1367  }
1368  }
1369 
1370  // do any scaling
1371  nsCString scaleString;
1372  rv = prop->GetScale(scaleString);
1373  NS_ENSURE_SUCCESS(rv, rv);
1374  if (!scaleString.IsEmpty()) {
1375  sbFraction scale;
1376  rv = sbFractionFromString(scaleString, scale);
1377  NS_ENSURE_SUCCESS(rv, rv);
1378  double val;
1379  rv = value->GetAsDouble(&val);
1380  NS_ENSURE_SUCCESS(rv, rv);
1381  val *= scale;
1382  nsCOMPtr<nsIWritableVariant> var = do_QueryInterface(value, &rv);
1383  NS_ENSURE_SUCCESS(rv, rv);
1384  rv = var->SetAsDouble(val);
1385  NS_ENSURE_SUCCESS(rv, rv);
1386  }
1387 
1388  // the gstreamer side wants the properties to be the right type :|
1389  switch (dataType) {
1390  case nsIDataType::VTYPE_INT32: {
1391  nsCOMPtr<nsIWritableVariant> var = do_QueryInterface(value, &rv);
1392  NS_ENSURE_SUCCESS(rv, rv);
1393  PRInt32 val;
1394  rv = var->GetAsInt32(&val);
1395  NS_ENSURE_SUCCESS(rv, rv);
1396  nsCOMPtr<nsIVariant> limit;
1397  rv = prop->GetValueMax(getter_AddRefs(limit));
1398  if (NS_SUCCEEDED(rv) && limit) {
1399  PRInt32 maxInt;
1400  rv = limit->GetAsInt32(&maxInt);
1401  if (NS_SUCCEEDED(rv) && val > maxInt) {
1402  val = maxInt;
1403  }
1404  }
1405  rv = prop->GetValueMin(getter_AddRefs(limit));
1406  if (NS_SUCCEEDED(rv) && limit) {
1407  PRInt32 minInt;
1408  rv = limit->GetAsInt32(&minInt);
1409  if (NS_SUCCEEDED(rv) && val < minInt) {
1410  val = minInt;
1411  }
1412  }
1413  rv = var->SetAsInt32(val);
1414  NS_ENSURE_SUCCESS(rv, rv);
1415  break;
1416  }
1417  }
1418 
1419  rv = aDstBag->SetProperty(propName, value);
1420  NS_ENSURE_SUCCESS(rv, rv);
1421  }
1422  NS_ENSURE_SUCCESS(rv, rv);
1423 
1424  return NS_OK;
1425 }
1426 
1427 NS_IMETHODIMP
1429 {
1430  TRACE(("%s[%p]", __FUNCTION__, this));
1431 
1432  if (mAvailableProfiles) {
1433  NS_IF_ADDREF (*aAvailableProfiles = mAvailableProfiles);
1434  return NS_OK;
1435  }
1436 
1437  /* If we haven't already cached it, then figure out what we have */
1438 
1439  if (!mElementNames.IsInitialized()) {
1440  PRBool initSuccess = mElementNames.Init();
1441  NS_ENSURE_TRUE(initSuccess, NS_ERROR_OUT_OF_MEMORY);
1442  }
1443 
1444  nsresult rv;
1445  PRBool hasMoreElements;
1446  nsCOMPtr<nsISimpleEnumerator> dirEnum;
1447 
1448  nsCOMPtr<nsIURI> profilesDirURI;
1449  rv = NS_NewURI(getter_AddRefs(profilesDirURI),
1450  NS_LITERAL_STRING("resource://app/gstreamer/encode-profiles"));
1451  NS_ENSURE_SUCCESS(rv, rv);
1452 
1453  nsCOMPtr<nsIFileURL> profilesDirFileURL =
1454  do_QueryInterface(profilesDirURI, &rv);
1455  NS_ENSURE_SUCCESS(rv, rv);
1456 
1457  nsCOMPtr<nsIFile> profilesDir;
1458  rv = profilesDirFileURL->GetFile(getter_AddRefs(profilesDir));
1459  NS_ENSURE_SUCCESS(rv, rv);
1460 
1461  nsCOMPtr<nsIMutableArray> array =
1462  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
1463  NS_ENSURE_SUCCESS(rv, rv);
1464 
1465  nsCOMPtr<sbITranscodeProfileLoader> profileLoader =
1466  do_CreateInstance("@songbirdnest.com/Songbird/Transcode/ProfileLoader;1",
1467  &rv);
1468  NS_ENSURE_SUCCESS (rv, rv);
1469 
1470  rv = profilesDir->GetDirectoryEntries(getter_AddRefs(dirEnum));
1471  NS_ENSURE_SUCCESS (rv, rv);
1472 
1473  while (PR_TRUE) {
1474  rv = dirEnum->HasMoreElements(&hasMoreElements);
1475  NS_ENSURE_SUCCESS(rv, rv);
1476  if (!hasMoreElements)
1477  break;
1478 
1479  nsCOMPtr<nsIFile> file;
1480  rv = dirEnum->GetNext(getter_AddRefs(file));
1481  NS_ENSURE_SUCCESS(rv, rv);
1482 
1483  nsCOMPtr<sbITranscodeProfile> profile;
1484 
1485  rv = profileLoader->LoadProfile(file, getter_AddRefs(profile));
1486  if (NS_FAILED(rv))
1487  continue;
1488 
1489  nsCOMPtr<sbITranscodeEncoderProfile> encoderProfile =
1490  do_QueryInterface(profile);
1491  NS_ENSURE_TRUE(encoderProfile, NS_ERROR_NO_INTERFACE);
1492  rv = EnsureProfileAvailable(encoderProfile);
1493  if (NS_FAILED(rv)) {
1494  // Not able to use this profile; don't return it.
1495  continue;
1496  }
1497 
1498  rv = array->AppendElement(encoderProfile, PR_FALSE);
1499  NS_ENSURE_SUCCESS (rv, rv);
1500  }
1501 
1502  mAvailableProfiles = do_QueryInterface(array, &rv);
1503  NS_ENSURE_SUCCESS (rv, rv);
1504 
1505  NS_ADDREF(*aAvailableProfiles = mAvailableProfiles);
1506 
1507  return NS_OK;
1508 }
1509 
1510 /* attribute duoble quality; */
1511 NS_IMETHODIMP
1512 sbGStreamerTranscodeDeviceConfigurator::GetQuality(double *aQuality)
1513 {
1514  TRACE(("%s[%p]", __FUNCTION__, this));
1515  NS_ENSURE_ARG_POINTER(aQuality);
1516  *aQuality = mQuality;
1517  return NS_OK;
1518 }
1519 
1520 NS_IMETHODIMP
1521 sbGStreamerTranscodeDeviceConfigurator::SetQuality(double aQuality)
1522 {
1523  TRACE(("%s[%p]", __FUNCTION__, this));
1524  NS_ENSURE_FALSE(mConfigurateState > CONFIGURATE_NOT_STARTED,
1525  NS_ERROR_ALREADY_INITIALIZED);
1526  mQuality = aQuality;
1527  return NS_OK;
1528 }
1529 
1530 /**** sbIDeviceTranscodingConfigurator implementation *****/
1531 /* attribute sbIDevice device; */
1532 NS_IMETHODIMP
1533 sbGStreamerTranscodeDeviceConfigurator::GetDevice(sbIDevice * *aDevice)
1534 {
1535  TRACE(("%s[%p]", __FUNCTION__, this));
1536  NS_ENSURE_ARG_POINTER(aDevice);
1537  NS_IF_ADDREF(*aDevice = mDevice);
1538  return NS_OK;
1539 }
1540 NS_IMETHODIMP
1541 sbGStreamerTranscodeDeviceConfigurator::SetDevice(sbIDevice * aDevice)
1542 {
1543  TRACE(("%s[%p]", __FUNCTION__, this));
1544  NS_ENSURE_FALSE(mConfigurateState > CONFIGURATE_NOT_STARTED,
1545  NS_ERROR_ALREADY_INITIALIZED);
1546  mDevice = aDevice;
1547  // clear the desired sizes
1548  mOutputDimensions = Dimensions();
1549  return NS_OK;
1550 }
1551 
1552 /**** sbITranscodingConfigurator implementation *****/
1553 
1554 /* void determineOutputType (); */
1555 NS_IMETHODIMP
1557 {
1558  TRACE(("%s[%p]", __FUNCTION__, this));
1559  // check our inputs
1560  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_INITIALIZED);
1561  NS_ENSURE_FALSE(mConfigurateState >= CONFIGURATE_OUTPUT_SET,
1562  NS_ERROR_ALREADY_INITIALIZED);
1563 
1564  nsresult rv;
1565 
1566  // Get the quality preference
1567  rv = SelectQuality();
1568  NS_ENSURE_SUCCESS(rv, rv);
1569 
1570  // Get the referred encoding profile
1571  rv = SelectProfile();
1572  NS_ENSURE_SUCCESS(rv, rv);
1573 
1575 
1576  return NS_OK;
1577 }
1578 
1579 /* void configurate (); */
1580 NS_IMETHODIMP
1582 {
1583  TRACE(("%s[%p]", __FUNCTION__, this));
1584  // check our inputs
1585  NS_ENSURE_TRUE(mInputFormat, NS_ERROR_NOT_INITIALIZED);
1586  NS_ENSURE_FALSE(mConfigurateState >= CONFIGURATE_FINISHED,
1587  NS_ERROR_ALREADY_INITIALIZED);
1588 
1589  nsresult rv;
1590 
1592  // no output set yet, do that now
1593  rv = DetermineOutputType();
1594  NS_ENSURE_SUCCESS(rv, rv);
1595  }
1596 
1597  // Get the audio parameters
1598  rv = SetAudioProperties();
1599  NS_ENSURE_SUCCESS(rv, rv);
1600  // Calculate video size
1602  NS_ENSURE_SUCCESS(rv, rv);
1603  if (NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA == rv) {
1604  // No video
1605  mVideoEncoder.SetIsVoid(PR_TRUE);
1606  }
1607  else {
1608  // Calculate video bitrate
1610  NS_ENSURE_SUCCESS(rv, rv);
1611  // Set video parameters
1612  rv = SetVideoProperties();
1613  NS_ENSURE_SUCCESS(rv, rv);
1614  }
1615 
1616  // all done
1618  return NS_OK;
1619 }
readonly attribute AString audioEncoder
The audio encoder to use. The name is specific to the transcoder (for example, this may be a gstreame...
nsresult MakeCapsFromAttributes(enum sbGstCapsMapType aType, const nsACString &aMimeType, nsIArray *aAttributes, GstCaps **aResultCaps)
return NS_OK
readonly attribute AString videoEncoder
The video encoder to use. The name is specific to the transcoder (for example, this may be a gstreame...
static nsresult GetDevCapRangeUpper(sbIDevCapRange *aRange, PRInt32 aTarget, PRInt32 *_retval)
inArray array
nsresult SB_NewTranscodeError(const nsAString &aMessageWithItem, const nsAString &aMessageWithoutItem, const nsAString &aDetails, const nsAString &aUri, sbIMediaItem *aMediaItem, sbITranscodeError **_retval)
nsresult sbFractionFromString(nsAString const &aString, sbFraction &aFraction)
Definition: sbFraction.h:259
nsDataHashtable< nsISupportsHashKey, EncoderProfileData > mElementNames
populateBox limit
Definition: tuner2.js:877
readonly attribute sbIMediaFormatAudio audioFormat
The basic audio format for data that is not specific to a particular codec.
Songbird Variant Utility Definitions.
PRUint32 Numerator() const
Definition: sbFraction.h:136
function width(ele) rect(ele).width
const unsigned long TRANSCODE_TYPE_AUDIO_VIDEO
nsCOMPtr< sbITranscodeEncoderProfile > mSelectedProfile
const char * propName
readonly attribute sbIMediaFormatVideo videoFormat
The basic video format for data that is not specific to a particular codec.
GstCaps * GetCapsForMimeType(const nsACString &aMimeType, enum sbGstCapsMapType aType)
var count
Definition: test_bug7406.js:32
nsresult CopyPropertiesIntoBag(nsIArray *aSrcProps, nsIWritablePropertyBag *aDstBag, PRBool aIsVideo)
nsCOMPtr< sbITranscodeError > mLastError
function num(elem, prop)
nsCOMPtr< sbIMediaFormat > mInputFormat
this _dialogInput val(dateText)
PRUint32 Denominator() const
Definition: sbFraction.h:144
_updateDatepicker height
nsCOMPtr< sbIMediaFormatVideo > mVideoFormat
nsCOMPtr< nsIWritablePropertyBag2 > mAudioEncoderProperties
const PR_UINT32_MAX
Definition: httpd.js:55
#define SB_MEDIAFORMATVIDEO_CONTRACTID
SimpleArrayEnumerator prototype hasMoreElements
static Dimensions GetMaximumFit(const Dimensions &aInput, const Dimensions &aMaximum)
bool IsEqual(sbFraction const &aOther) const
Definition: sbFraction.h:69
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
long GetValue(in unsigned long aIndex)
nsCOMPtr< nsIWritablePropertyBag2 > mVideoEncoderProperties
var hidden
NS_IMPL_ISUPPORTS_INHERITED2(sbGStreamerTranscodeDeviceConfigurator, sbTranscodingConfigurator, sbIDeviceTranscodingConfigurator, sbPIGstTranscodingConfigurator)
attribute sbIMediaFormat inputFormat
The input format to use when configuring the transcode profile.
#define min(a, b)
nsresult EnsureProfileAvailable(sbITranscodeEncoderProfile *aProfile)
nsCOMPtr< sbIMediaFormatAudio > mAudioFormat
#define SB_MEDIAFORMATAUDIO_CONTRACTID
_getSelectedPageStyle s i
const char * FindMatchingElementName(const char *srcCapsString, const char *typeName)
var file
function range(x, y)
Definition: httpd.js:138