sbGStreamerTranscodeAudioConfigurator.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-20010 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 
28 
30 #include <nsIArray.h>
31 #include <nsIFile.h>
32 #include <nsIFileURL.h>
33 #include <nsIMutableArray.h>
34 #include <nsIURI.h>
35 #include <nsIVariant.h>
36 #include <nsIWritablePropertyBag.h>
37 #include <nsIWritablePropertyBag2.h>
38 
40 #include <sbIDevice.h>
41 #include <sbIDeviceCapabilities.h>
42 #include <sbIMediaFormatMutable.h>
43 #include <sbITranscodeError.h>
44 #include <sbITranscodeManager.h>
45 #include <sbITranscodeProfile.h>
46 
48 #include <nsCOMArray.h>
49 #include <nsComponentManagerUtils.h>
50 #include <nsNetUtil.h>
51 #include <nsArrayUtils.h>
52 #include <prlog.h>
53 
55 #include <sbArrayUtils.h>
56 #include <sbMemoryUtils.h>
57 #include <sbStringUtils.h>
58 #include <sbTranscodeUtils.h>
59 #include <sbVariantUtils.h>
60 #include <sbFraction.h>
61 #include <sbPrefBranch.h>
62 
64 #if _MSC_VER
65 # pragma warning (push)
66 # pragma warning (disable: 4244)
67 #endif /* _MSC_VER */
68 #include <gst/gst.h>
69 #if _MSC_VER
70 # pragma warning (pop)
71 #endif /* _MSC_VER */
72 
74 #include <algorithm>
75 #include <functional>
76 #include <vector>
77 
80 
82 
86 #ifdef PR_LOGGING
87 static PRLogModuleInfo* gGstTranscodeConfiguratorLog = nsnull;
88 #define TRACE(args) PR_LOG(gGstTranscodeConfiguratorLog, PR_LOG_DEBUG, args)
89 #define LOG(args) PR_LOG(gGstTranscodeConfiguratorLog, PR_LOG_WARN, args)
90 #if __GNUC__
91 #define __FUNCTION__ __PRETTY_FUNCTION__
92 #endif /* __GNUC__ */
93 #else
94 #define TRACE(args) /* nothing */
95 #define LOG(args) /* nothing */
96 #endif /* PR_LOGGING */
97 
99 
110 static nsresult
111 GetDevCapRangeUpper(sbIDevCapRange *aRange, PRInt32 aTarget, PRInt32 *_retval)
112 {
113  TRACE(("%s", __FUNCTION__));
114  NS_ENSURE_ARG_POINTER(aRange);
115  NS_ENSURE_ARG_POINTER(_retval);
116 
117  nsresult rv;
118  PRUint32 count;
119  PRInt32 result = PR_INT32_MIN, max = PR_INT32_MIN;
120  rv = aRange->GetValueCount(&count);
121  NS_ENSURE_SUCCESS(rv, rv);
122  if (count > 0) {
123  for (PRUint32 i = 0; i < count; ++i) {
124  PRInt32 v;
125  rv = aRange->GetValue(i, &v);
126  NS_ENSURE_SUCCESS(rv, rv);
127  if ((result < aTarget || v < result) && v >= aTarget) {
128  result = v;
129  }
130  else if (max < v) {
131  max = v;
132  }
133  }
134  if (result >= aTarget) {
135  *_retval = result;
136  return NS_OK;
137  }
138  else {
139  *_retval = max;
140  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
141  }
142  }
143 
144  // no count, use min + step + max
145  PRInt32 min, step;
146  rv = aRange->GetMin(&min);
147  rv |= aRange->GetStep(&step);
148  rv |= aRange->GetMax(&max);
149  NS_ENSURE_SUCCESS(rv, rv);
150  NS_ENSURE_TRUE(step > 0, NS_ERROR_UNEXPECTED);
151  if (max < aTarget) {
152  *_retval = max;
153  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
154  }
155  if (min > aTarget) {
156  *_retval = min;
157  return NS_OK;
158  }
159  // _retval = min + ceil((target - min) / step) * step
160  *_retval = min + (aTarget - min + step - 1) / step * step;
161  return NS_OK;
162 
163 }
164 
169 
171  mProfileFromPrefs(PR_FALSE),
172  mProfileFromGlobalPrefs(PR_FALSE)
173 {
174  #if PR_LOGGING
175  if (!gGstTranscodeConfiguratorLog) {
176  gGstTranscodeConfiguratorLog =
177  PR_NewLogModule("sbGStreamerTranscodeAudioConfigurator");
178  }
179  #endif /* PR_LOGGING */
180  TRACE(("%s[%p]", __FUNCTION__, this));
181  /* nothing */
182 }
183 
185 {
186  /* nothing */
187 }
188 
197 static nsresult
199  const nsACString& aMimeType,
200  nsIArray *aAttributes,
201  GstCaps** aResultCaps)
202 {
203  TRACE(("%s", __FUNCTION__));
204  NS_ENSURE_ARG_POINTER(aAttributes);
205  NS_ENSURE_ARG_POINTER(aResultCaps);
206 
207  nsresult rv;
208 
209  nsCOMPtr<nsISimpleEnumerator> attrsEnum;
210  rv = aAttributes->Enumerate(getter_AddRefs(attrsEnum));
211  NS_ENSURE_SUCCESS(rv, rv);
212 
213  sbGstCaps caps = GetCapsForMimeType (aMimeType, aType);
214  NS_ENSURE_TRUE(caps, NS_ERROR_FAILURE);
215  GstStructure* capsStruct = gst_caps_get_structure(caps.get(), 0);
216 
217  PRBool hasMore;
218  while (NS_SUCCEEDED(rv = attrsEnum->HasMoreElements(&hasMore)) && hasMore) {
219  nsCOMPtr<nsISupports> attrSupports;
220  rv = attrsEnum->GetNext(getter_AddRefs(attrSupports));
221  NS_ENSURE_SUCCESS(rv, rv);
222  nsCOMPtr<sbITranscodeProfileAttribute> attr =
223  do_QueryInterface(attrSupports, &rv);
224  NS_ENSURE_SUCCESS(rv, rv);
225  nsString attrName;
226  rv = attr->GetName(attrName);
227  NS_ENSURE_SUCCESS(rv, rv);
228  nsCOMPtr<nsIVariant> attrValue;
229  rv = attr->GetValue(getter_AddRefs(attrValue));
230  NS_ENSURE_SUCCESS(rv, rv);
231  PRUint16 attrType;
232  rv = attrValue->GetDataType(&attrType);
233  NS_ENSURE_SUCCESS(rv, rv);
234  switch(attrType) {
235  case nsIDataType::VTYPE_INT8: case nsIDataType::VTYPE_UINT8:
236  case nsIDataType::VTYPE_INT16: case nsIDataType::VTYPE_UINT16:
237  case nsIDataType::VTYPE_INT32: case nsIDataType::VTYPE_UINT32:
238  case nsIDataType::VTYPE_INT64: case nsIDataType::VTYPE_UINT64:
239  {
240  PRInt32 intValue;
241  rv = attrValue->GetAsInt32(&intValue);
242  NS_ENSURE_SUCCESS(rv, rv);
243  gst_structure_set(capsStruct,
244  NS_LossyConvertUTF16toASCII(attrName).get(),
245  G_TYPE_INT,
246  intValue,
247  NULL);
248  break;
249  }
250  case nsIDataType::VTYPE_UTF8STRING:
251  case nsIDataType::VTYPE_DOMSTRING:
252  case nsIDataType::VTYPE_CSTRING:
253  case nsIDataType::VTYPE_ASTRING:
254  {
255  nsCString stringValue;
256  rv = attrValue->GetAsACString(stringValue);
257  NS_ENSURE_SUCCESS (rv, rv);
258 
259  gst_structure_set(capsStruct,
260  NS_LossyConvertUTF16toASCII(attrName).get(),
261  G_TYPE_STRING,
262  stringValue.BeginReading(),
263  NULL);
264  break;
265  }
266  default:
267  NS_NOTYETIMPLEMENTED("Unknown attribute type");
268  }
269  }
270  NS_ENSURE_SUCCESS(rv, rv);
271 
272  *aResultCaps = caps.forget();
273  return NS_OK;
274 }
275 
276 /* Check that the profile is the right type, and that we have appropriate
277  GStreamer elements to use it. Returns NS_OK if the profile appears to be
278  useable.
279  */
280 nsresult
282 {
283  TRACE(("%s[%p]", __FUNCTION__, this));
284  NS_ENSURE_ARG_POINTER(aProfile);
285 
286  nsresult rv;
287 
288  // Only get audio profiles
289  PRUint32 type;
290  rv = aProfile->GetType(&type);
291  NS_ENSURE_SUCCESS(rv, rv);
292 
294  return NS_ERROR_NOT_AVAILABLE;
295 
296  EncoderProfileData elementNames;
297 
298  // check that we have a muxer available
299  nsString capsName;
300  rv = aProfile->GetContainerFormat(capsName);
301  NS_ENSURE_SUCCESS(rv, rv);
302  if (!capsName.IsEmpty()) {
303  nsCOMPtr<nsIArray> attrs;
304  rv = aProfile->GetContainerAttributes(getter_AddRefs(attrs));
305  NS_ENSURE_SUCCESS(rv, rv);
306 
307  GstCaps* caps = NULL;
309  NS_LossyConvertUTF16toASCII(capsName),
310  attrs,
311  &caps);
312  NS_ENSURE_SUCCESS(rv, rv);
313 
314  const char* muxerCodecName = FindMatchingElementName(caps, "Muxer");
315  if (!muxerCodecName) {
316  // things like id3 are called Formatter instead, but are the same for
317  // our purposes
318  muxerCodecName = FindMatchingElementName(caps, "Formatter");
319  }
320  gst_caps_unref(caps);
321  if (!muxerCodecName) {
322  TRACE(("no muxer available for %s",
323  NS_LossyConvertUTF16toASCII(capsName).get()));
324  return NS_ERROR_UNEXPECTED;
325  }
326  elementNames.muxer = muxerCodecName;
327  }
328 
330  rv = aProfile->GetAudioCodec(capsName);
331  NS_ENSURE_SUCCESS(rv, rv);
332  if (!capsName.IsEmpty()) {
333  nsCOMPtr<nsIArray> attrs;
334  rv = aProfile->GetAudioAttributes(getter_AddRefs(attrs));
335  NS_ENSURE_SUCCESS(rv, rv);
336 
337  GstCaps* caps = NULL;
339  NS_LossyConvertUTF16toASCII(capsName),
340  attrs,
341  &caps);
342  NS_ENSURE_SUCCESS(rv, rv);
343 
344  const char* audioEncoder = FindMatchingElementName(caps, "Encoder");
345  gst_caps_unref(caps);
346  if (!audioEncoder) {
347  TRACE(("no audio encoder available for %s",
348  NS_LossyConvertUTF16toASCII(capsName).get()));
349  return NS_ERROR_UNEXPECTED;
350  }
351  elementNames.audioEncoder = audioEncoder;
352  }
353 
354  PRBool success = mElementNames.Put(aProfile, elementNames);
355  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
356 
357  return NS_OK;
358 }
359 
360 /* Check if this profile is useable by the device.
361  *
362  * If successful, aFormat will contain the format object constraining the
363  * device appropriate for this profile.
364  */
365 nsresult
367  sbITranscodeProfile *aProfile,
368  sbIAudioFormatType **aFormat)
369 {
370  TRACE(("%s[%p]", __FUNCTION__, this));
371 
372  // get the device caps
373  nsresult rv;
374  nsCOMPtr<sbIDeviceCapabilities> caps;
375  rv = mDevice->GetCapabilities(getter_AddRefs(caps));
376  NS_ENSURE_SUCCESS(rv, rv);
377 
378  PRUint32 mimeTypesCount;
379  char **mimeTypes;
380  rv = caps->GetSupportedMimeTypes(sbIDeviceCapabilities::CONTENT_AUDIO,
381  &mimeTypesCount,
382  &mimeTypes);
383  NS_ENSURE_SUCCESS(rv, rv);
384 
385  if (mimeTypesCount == 0) {
386  // If our device supports zero audio formats, but we're being asked to do
387  // an audio transcode, then just accept all profiles without a specific
388  // format type. This is used for CD ripping, for example.
389  *aFormat = NULL;
390  return NS_OK;
391  }
392 
393  sbAutoFreeXPCOMArrayByRef<char**> autoMimeTypes(mimeTypesCount, mimeTypes);
394 
395  for (PRUint32 mimeTypeIndex = 0;
396  mimeTypeIndex < mimeTypesCount;
397  ++mimeTypeIndex)
398  {
399  nsISupports** formatTypes;
400  PRUint32 formatTypeCount;
401  rv = caps->GetFormatTypes(sbIDeviceCapabilities::CONTENT_AUDIO,
402  NS_ConvertASCIItoUTF16(mimeTypes[mimeTypeIndex]),
403  &formatTypeCount,
404  &formatTypes);
405  NS_ENSURE_SUCCESS(rv, rv);
406  sbAutoFreeXPCOMPointerArray<nsISupports> freeFormats(formatTypeCount,
407  formatTypes);
408 
409  for (PRUint32 formatIndex = 0;
410  formatIndex < formatTypeCount;
411  formatIndex++)
412  {
413  nsCOMPtr<sbIAudioFormatType> format = do_QueryInterface(
414  formatTypes[formatIndex], &rv);
415  NS_ENSURE_SUCCESS(rv, rv);
416 
417  // check the container
418  nsCString formatContainer;
419  rv = format->GetContainerFormat(formatContainer);
420  NS_ENSURE_SUCCESS(rv, rv);
421  nsString encoderContainer;
422  rv = aProfile->GetContainerFormat(encoderContainer);
423  NS_ENSURE_SUCCESS(rv, rv);
424 
425  if (!encoderContainer.Equals(NS_ConvertUTF8toUTF16(formatContainer))) {
426  // mismatch, try the next device format
427  continue;
428  }
429 
430  // check the audio codec
431  nsString encoderAudioCodec;
432  rv = aProfile->GetAudioCodec(encoderAudioCodec);
433  NS_ENSURE_SUCCESS(rv, rv);
434 
435  nsCString formatAudioCodec;
436  rv = format->GetAudioCodec(formatAudioCodec);
437  NS_ENSURE_SUCCESS(rv, rv);
438  if (!encoderAudioCodec.Equals(NS_ConvertUTF8toUTF16(formatAudioCodec))) {
439  // mismatch, try the next device format
440  continue;
441  }
442 
443  // If we got to here, it's compatible.
444  NS_ADDREF (*aFormat = format);
445  return NS_OK;
446  }
447  }
448 
449  return NS_ERROR_FAILURE;
450 }
451 
460 nsresult
462 {
463  TRACE(("%s[%p]", __FUNCTION__, this));
464 
465  NS_PRECONDITION(mDevice,
466  "attempted to select profile with no device");
467 
468  nsCOMPtr<sbIAudioFormatType> prefFormat;
469  nsCOMPtr<sbIAudioFormatType> globalPrefFormat;
470  nsCOMPtr<sbIAudioFormatType> bestFormat;
471  nsCOMPtr<sbITranscodeProfile> prefProfile;
472  nsCOMPtr<sbITranscodeProfile> globalPrefProfile;
473  nsCOMPtr<sbITranscodeProfile> bestProfile;
474  PRBool hasProfilePref = PR_FALSE;
475  PRBool hasGlobalProfilePref = PR_FALSE;
476  PRUint32 bestPriority = 0;
477  nsresult rv;
478 
479  // See if we have a preference for the transcoding profile. Check for a device
480  // specific preference, or if not present, look for a global one.
481  nsString prefProfileId;
482  nsString globalPrefProfileId;
483 
484  nsCOMPtr<nsIVariant> profileIdVariant;
485  rv = mDevice->GetPreference(NS_LITERAL_STRING("transcode_profile.profile_id"),
486  getter_AddRefs(profileIdVariant));
487  if (NS_SUCCEEDED(rv))
488  {
489  rv = profileIdVariant->GetAsAString(prefProfileId);
490  NS_ENSURE_SUCCESS(rv, rv);
491 
492  if (!prefProfileId.IsEmpty()) {
493  TRACE(("%s: found a profile", __FUNCTION__));
494  hasProfilePref = PR_TRUE;
495  }
496  }
497 
498  if (!hasProfilePref) {
499  sbPrefBranch prefs("songbird.device.transcode_profile.", &rv);
500  NS_ENSURE_SUCCESS (rv, rv);
501 
502  rv = prefs.GetPreference(NS_LITERAL_STRING("profile_id"),
503  getter_AddRefs(profileIdVariant));
504  if (NS_SUCCEEDED(rv)) {
505  rv = profileIdVariant->GetAsAString(globalPrefProfileId);
506  NS_ENSURE_SUCCESS(rv, rv);
507 
508  if (!globalPrefProfileId.IsEmpty()) {
509  TRACE(("%s: found a global pref for profile", __FUNCTION__));
510  hasGlobalProfilePref = PR_TRUE;
511  }
512  }
513  }
514 
515  // get available profiles (these are the ones that we actually have
516  // appropriate encoders/etc for)
517  nsCOMPtr<nsIArray> profilesArray;
518  rv = GetAvailableProfiles(getter_AddRefs(profilesArray));
519  NS_ENSURE_SUCCESS(rv, rv);
520  nsCOMPtr<nsISimpleEnumerator> profilesEnum;
521  rv = profilesArray->Enumerate(getter_AddRefs(profilesEnum));
522  NS_ENSURE_SUCCESS(rv, rv);
523 
524  // Find the profile from the pref, or otherwise the highest-ranked one.
525  PRBool hasMoreProfiles;
526  while (NS_SUCCEEDED(profilesEnum->HasMoreElements(&hasMoreProfiles)) &&
527  hasMoreProfiles)
528  {
529  nsCOMPtr<nsISupports> profileSupports;
530  rv = profilesEnum->GetNext(getter_AddRefs(profileSupports));
531  NS_ENSURE_SUCCESS(rv, rv);
532 
533  nsCOMPtr<sbITranscodeProfile> profile =
534  do_QueryInterface(profileSupports, &rv);
535  NS_ENSURE_SUCCESS(rv, rv);
536 
537  nsString profileId;
538  rv = profile->GetId(profileId);
539  NS_ENSURE_SUCCESS(rv, rv);
540 
541  if (hasProfilePref) {
542  if (profileId.Equals(prefProfileId)) {
543  nsCOMPtr<sbIAudioFormatType> deviceFormat;
544  rv = CheckProfileSupportedByDevice (profile,
545  getter_AddRefs(deviceFormat));
546  if (NS_SUCCEEDED (rv)) {
547  prefProfile = profile;
548  prefFormat = deviceFormat;
549  }
550  }
551  }
552 
553  if (hasGlobalProfilePref) {
554  if (profileId.Equals(globalPrefProfileId)) {
555  nsCOMPtr<sbIAudioFormatType> deviceFormat;
556  rv = CheckProfileSupportedByDevice (profile,
557  getter_AddRefs(deviceFormat));
558  if (NS_SUCCEEDED (rv)) {
559  globalPrefProfile = profile;
560  globalPrefFormat = deviceFormat;
561  }
562  }
563  }
564 
565  // Also track the highest-priority profile. This is our default if there is
566  // no preference set or the preferenced profile is unavailable.
567  PRUint32 priority;
568  rv = profile->GetPriority(&priority);
569  NS_ENSURE_SUCCESS(rv, rv);
570 
571  if (!bestProfile || priority > bestPriority) {
572  nsCOMPtr<sbIAudioFormatType> deviceFormat;
573  rv = CheckProfileSupportedByDevice (profile,
574  getter_AddRefs(deviceFormat));
575  if (NS_SUCCEEDED (rv)) {
576  bestProfile = profile;
577  bestPriority = priority;
578  bestFormat = deviceFormat;
579  }
580  }
581  }
582 
583  if (prefProfile) {
584  LOG(("Using device pref profile"));
585  mProfileFromPrefs = PR_TRUE;
586  mSelectedProfile = prefProfile;
587  mSelectedFormat = prefFormat;
588  }
589  else if (globalPrefProfile) {
590  LOG(("Using global pref profile"));
591  mProfileFromGlobalPrefs = PR_TRUE;
592  mSelectedProfile = globalPrefProfile;
593  mSelectedFormat = globalPrefFormat;
594  }
595  else if (bestProfile) {
596  LOG(("Using best available profile"));
597  mSelectedProfile = bestProfile;
598  mSelectedFormat = bestFormat;
599  }
600  else {
601  // No usable profiles at all.
602  return NS_ERROR_FAILURE;
603  }
604 
605  return NS_OK;
606 }
607 
611 nsresult
613 {
614  TRACE(("%s[%p]", __FUNCTION__, this));
615  NS_PRECONDITION(mSelectedProfile,
616  "attempted to set audio properties without selecting profile");
617 
618  nsresult rv;
619 
620  if (!mAudioFormat) {
621  mAudioFormat = do_CreateInstance(SB_MEDIAFORMATAUDIO_CONTRACTID, &rv);
622  NS_ENSURE_SUCCESS(rv, rv);
623  }
624 
625  nsCOMPtr<sbIMediaFormatAudioMutable> audioFormat =
626  do_QueryInterface(mAudioFormat, &rv);
627  NS_ENSURE_SUCCESS(rv, rv);
628 
629  nsCOMPtr<sbIMediaFormatAudio> inputFormat;
630  rv = mInputFormat->GetAudioStream(getter_AddRefs(inputFormat));
631  NS_ENSURE_SUCCESS(rv, rv);
632 
633  // This shouldn't be called if we don't have audio input
634  if (!inputFormat)
635  return NS_ERROR_UNEXPECTED;
636 
637  if (!mSelectedFormat) {
638  // If we don't have a selected format, we force the output rate/channels
639  // to be the same as the input
640  PRInt32 sampleRate;
641  rv = inputFormat->GetSampleRate(&sampleRate);
642  NS_ENSURE_SUCCESS(rv, rv);
643  rv = audioFormat->SetSampleRate(sampleRate);
644  NS_ENSURE_SUCCESS(rv, rv);
645 
646  PRInt32 channels;
647  rv = inputFormat->GetChannels(&channels);
648  NS_ENSURE_SUCCESS(rv, rv);
649  rv = audioFormat->SetChannels(channels);
650  NS_ENSURE_SUCCESS(rv, rv);
651 
652  return NS_OK;
653  }
654 
655  // Otherwise, we have a selected format: force these in range.
656 
657  PRBool isInRange;
658 
659  nsCOMPtr<sbIDevCapRange> sampleRateRange;
660  rv = mSelectedFormat->GetSupportedSampleRates(getter_AddRefs(sampleRateRange));
661  NS_ENSURE_SUCCESS(rv, rv);
662  PRInt32 sampleRate;
663  rv = inputFormat->GetSampleRate(&sampleRate);
664  NS_ENSURE_SUCCESS(rv, rv);
665  rv = sampleRateRange->IsValueInRange(sampleRate, &isInRange);
666  if (NS_FAILED(rv) || !isInRange) {
667  // won't fit; pick anything for now
668  rv = GetDevCapRangeUpper(sampleRateRange, sampleRate, &sampleRate);
669  NS_ENSURE_SUCCESS(rv, rv);
670  }
671  rv = audioFormat->SetSampleRate(sampleRate);
672  NS_ENSURE_SUCCESS(rv, rv);
673 
674  nsCOMPtr<sbIDevCapRange> channelsRange;
675  rv = mSelectedFormat->GetSupportedChannels(getter_AddRefs(channelsRange));
676  NS_ENSURE_SUCCESS(rv, rv);
677  PRInt32 channels;
678  rv = inputFormat->GetChannels(&channels);
679  NS_ENSURE_SUCCESS(rv, rv);
680  rv = channelsRange->IsValueInRange(channels, &isInRange);
681  if (NS_FAILED(rv) || !isInRange) {
682  // won't fit; pick anything for now
683  PRInt32 newChannels;
684  rv = GetDevCapRangeUpper(channelsRange, channels, &newChannels);
685  if (NS_SUCCEEDED(rv)) {
686  channels = newChannels;
687  }
688  else {
689  // no channel information; assume supports mono + stereo
690  channels = (channels < 2 ? 1 : 2);
691  }
692  }
693  rv = audioFormat->SetChannels(channels);
694  NS_ENSURE_SUCCESS(rv, rv);
695 
696  return NS_OK;
697 }
698 
705 nsresult
707 {
708  TRACE(("%s[%p]", __FUNCTION__, this));
709  NS_PRECONDITION(mSelectedProfile,
710  "attempted to set audio properties without selecting profile");
711 
712  nsresult rv;
713 
716  do_CreateInstance("@songbirdnest.com/moz/xpcom/sbpropertybag;1", &rv);
717  NS_ENSURE_SUCCESS(rv, rv);
718  }
719 
720  nsCOMPtr<nsIWritablePropertyBag> writableBag =
721  do_QueryInterface(mAudioEncoderProperties, &rv);
722  NS_ENSURE_SUCCESS(rv, rv);
723 
724  nsCOMPtr<nsIArray> propsSrc;
725  rv = mSelectedProfile->GetAudioProperties(getter_AddRefs(propsSrc));
726  NS_ENSURE_SUCCESS(rv, rv);
727 
728  // If we selected the profile from the preferences, also apply the properties
729  // set in those prefs.
730  if (mProfileFromPrefs) {
732  mDevice,
733  propsSrc,
734  NS_LITERAL_STRING("transcode_profile.audio_properties"));
735  NS_ENSURE_SUCCESS(rv, rv);
736  }
737  else if (mProfileFromGlobalPrefs) {
739  nsnull,
740  propsSrc,
741  NS_LITERAL_STRING("songbird.device.transcode_profile.audio_properties"));
742  NS_ENSURE_SUCCESS(rv, rv);
743  }
744 
745  rv = CopyPropertiesIntoBag(propsSrc, writableBag, PR_FALSE);
746  NS_ENSURE_SUCCESS(rv, rv);
747 
748  return NS_OK;
749 }
750 
751 nsresult
753  sbIDevice *aDevice,
754  nsIArray *aPropertyArray,
755  nsString aPrefNameBase)
756 {
757  nsresult rv;
758 
759  if(!aPropertyArray) {
760  // Perfectly ok; this just means there aren't any properties.
761  return NS_OK;
762  }
763 
764  PRUint32 numProperties;
765  rv = aPropertyArray->GetLength(&numProperties);
766  NS_ENSURE_SUCCESS(rv, rv);
767 
768  for (PRUint32 i = 0; i < numProperties; i++) {
769  nsCOMPtr<sbITranscodeProfileProperty> property =
770  do_QueryElementAt(aPropertyArray, i, &rv);
771  NS_ENSURE_SUCCESS(rv, rv);
772 
773  nsString propName;
774  rv = property->GetPropertyName(propName);
775  NS_ENSURE_SUCCESS(rv, rv);
776 
777  nsCOMPtr<nsIVariant> prefVariant;
778 
779  if (aDevice) {
780  nsString prefName = aPrefNameBase;
781  prefName.AppendLiteral(".");
782  prefName.Append(propName);
783 
784  rv = aDevice->GetPreference(prefName, getter_AddRefs(prefVariant));
785  NS_ENSURE_SUCCESS(rv, rv);
786  }
787  else {
788  sbPrefBranch prefs(NS_ConvertUTF16toUTF8(aPrefNameBase).get(), &rv);
789  NS_ENSURE_SUCCESS(rv, rv);
790 
791  rv = prefs.GetPreference(propName, getter_AddRefs(prefVariant));
792  NS_ENSURE_SUCCESS(rv, rv);
793  }
794 
795  // Only use the property if we have a real value (not a void/empty variant)
796  PRUint16 dataType;
797  rv = prefVariant->GetDataType(&dataType);
798  NS_ENSURE_SUCCESS(rv, rv);
799  if (dataType != nsIDataType::VTYPE_VOID &&
800  dataType != nsIDataType::VTYPE_EMPTY)
801  {
802  rv = property->SetValue(prefVariant);
803  NS_ENSURE_SUCCESS(rv, rv);
804  }
805  }
806 
807  return NS_OK;
808 }
809 
810 
817 nsresult
819  nsIWritablePropertyBag * aDstBag,
820  PRBool aIsVideo)
821 {
822  NS_ENSURE_ARG_POINTER(aSrcProps);
823  NS_ENSURE_ARG_POINTER(aDstBag);
824 
825  nsresult rv;
826 
827  nsCOMPtr<nsISimpleEnumerator> propsEnum;
828  rv = aSrcProps->Enumerate(getter_AddRefs(propsEnum));
829  NS_ENSURE_SUCCESS(rv, rv);
830  PRBool hasMore;
831  while (NS_SUCCEEDED(rv = propsEnum->HasMoreElements(&hasMore)) && hasMore) {
832  nsCOMPtr<nsISupports> supports;
833  rv = propsEnum->GetNext(getter_AddRefs(supports));
834  NS_ENSURE_SUCCESS(rv, rv);
835  nsCOMPtr<sbITranscodeProfileProperty> prop =
836  do_QueryInterface(supports, &rv);
837  NS_ENSURE_SUCCESS(rv, rv);
838  PRBool hidden;
839  rv = prop->GetHidden(&hidden);
840  NS_ENSURE_SUCCESS(rv, rv);
841  if (hidden) {
842  continue;
843  }
844  nsString propName;
845  rv = prop->GetPropertyName(propName);
846  NS_ENSURE_SUCCESS(rv, rv);
847 
848  // get the value
849  nsCOMPtr<nsIVariant> value;
850  rv = prop->GetValue(getter_AddRefs(value));
851  NS_ENSURE_SUCCESS(rv, rv);
852 
853  rv = aDstBag->SetProperty(propName, value);
854  NS_ENSURE_SUCCESS(rv, rv);
855  }
856  NS_ENSURE_SUCCESS(rv, rv);
857 
858  return NS_OK;
859 }
860 
861 NS_IMETHODIMP
863 {
864  TRACE(("%s[%p]", __FUNCTION__, this));
865  if (mAvailableProfiles) {
866  NS_IF_ADDREF (*aAvailableProfiles = mAvailableProfiles);
867  return NS_OK;
868  }
869 
870  /* If we haven't already cached it, then figure out what we have */
871 
872  if (!mElementNames.IsInitialized()) {
873  PRBool initSuccess = mElementNames.Init();
874  NS_ENSURE_TRUE(initSuccess, NS_ERROR_OUT_OF_MEMORY);
875  }
876 
877  nsresult rv;
878  PRBool hasMoreElements;
879  nsCOMPtr<nsISimpleEnumerator> dirEnum;
880 
881  nsCOMPtr<nsIURI> profilesDirURI;
882  rv = NS_NewURI(getter_AddRefs(profilesDirURI),
883  NS_LITERAL_STRING("resource://app/gstreamer/encode-profiles"));
884  NS_ENSURE_SUCCESS(rv, rv);
885 
886  nsCOMPtr<nsIFileURL> profilesDirFileURL =
887  do_QueryInterface(profilesDirURI, &rv);
888  NS_ENSURE_SUCCESS(rv, rv);
889 
890  nsCOMPtr<nsIFile> profilesDir;
891  rv = profilesDirFileURL->GetFile(getter_AddRefs(profilesDir));
892  NS_ENSURE_SUCCESS(rv, rv);
893 
894  nsCOMPtr<nsIMutableArray> array =
895  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
896  NS_ENSURE_SUCCESS(rv, rv);
897 
898  nsCOMPtr<sbITranscodeProfileLoader> profileLoader =
899  do_CreateInstance("@songbirdnest.com/Songbird/Transcode/ProfileLoader;1",
900  &rv);
901  NS_ENSURE_SUCCESS (rv, rv);
902 
903  rv = profilesDir->GetDirectoryEntries(getter_AddRefs(dirEnum));
904  NS_ENSURE_SUCCESS (rv, rv);
905 
906  while (PR_TRUE) {
907  rv = dirEnum->HasMoreElements(&hasMoreElements);
908  NS_ENSURE_SUCCESS(rv, rv);
909  if (!hasMoreElements)
910  break;
911 
912  nsCOMPtr<nsIFile> file;
913  rv = dirEnum->GetNext(getter_AddRefs(file));
914  NS_ENSURE_SUCCESS(rv, rv);
915 
916  nsCOMPtr<sbITranscodeProfile> profile;
917 
918  rv = profileLoader->LoadProfile(file, getter_AddRefs(profile));
919  if (NS_FAILED(rv))
920  continue;
921 
922  rv = EnsureProfileAvailable(profile);
923  if (NS_FAILED(rv)) {
924  // Not able to use this profile; don't return it.
925  continue;
926  }
927 
928  rv = array->AppendElement(profile, PR_FALSE);
929  NS_ENSURE_SUCCESS (rv, rv);
930  }
931 
932  mAvailableProfiles = do_QueryInterface(array, &rv);
933  NS_ENSURE_SUCCESS (rv, rv);
934 
935  NS_ADDREF(*aAvailableProfiles = mAvailableProfiles);
936 
937  return NS_OK;
938 }
939 
940 /**** sbIDeviceTranscodingConfigurator implementation *****/
941 /* attribute sbIDevice device; */
942 NS_IMETHODIMP
943 sbGStreamerTranscodeAudioConfigurator::GetDevice(sbIDevice * *aDevice)
944 {
945  TRACE(("%s[%p]", __FUNCTION__, this));
946  NS_ENSURE_ARG_POINTER(aDevice);
947  NS_IF_ADDREF(*aDevice = mDevice);
948  return NS_OK;
949 }
950 
951 NS_IMETHODIMP
952 sbGStreamerTranscodeAudioConfigurator::SetDevice(sbIDevice * aDevice)
953 {
954  TRACE(("%s[%p]", __FUNCTION__, this));
955  NS_ENSURE_FALSE(mConfigurateState > CONFIGURATE_NOT_STARTED,
956  NS_ERROR_ALREADY_INITIALIZED);
957  mDevice = aDevice;
958  return NS_OK;
959 }
960 
961 /**** sbITranscodingConfigurator implementation *****/
962 
963 /* void determineOutputType (); */
964 NS_IMETHODIMP
966 {
967  TRACE(("%s[%p]", __FUNCTION__, this));
968  // check our inputs
969  NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_INITIALIZED);
970  NS_ENSURE_FALSE(mConfigurateState >= CONFIGURATE_OUTPUT_SET,
971  NS_ERROR_ALREADY_INITIALIZED);
972 
973  nsresult rv;
974 
975  // Get the preferred encoding profile
976  rv = SelectProfile();
977  NS_ENSURE_SUCCESS(rv, rv);
978 
979  EncoderProfileData elementNames;
980  PRBool success = mElementNames.Get(mSelectedProfile, &elementNames);
981  NS_ENSURE_TRUE (success, NS_ERROR_UNEXPECTED);
982  CopyASCIItoUTF16(elementNames.muxer, mMuxer);
983  CopyASCIItoUTF16(elementNames.audioEncoder, mAudioEncoder);
984 
985  if (!mMuxer.IsEmpty())
986  mUseMuxer = PR_TRUE;
987 
988  if (!mAudioEncoder.IsEmpty())
989  mUseAudioEncoder = PR_TRUE;
990 
991  rv = mSelectedProfile->GetFileExtension(mFileExtension);
992  NS_ENSURE_SUCCESS (rv, rv);
993 
995 
996  return NS_OK;
997 }
998 
999 /* void configurate (); */
1000 
1001 NS_IMETHODIMP
1003 {
1004  TRACE(("%s[%p]", __FUNCTION__, this));
1005  // check our inputs
1006  NS_ENSURE_TRUE(mInputFormat, NS_ERROR_NOT_INITIALIZED);
1007  NS_ENSURE_FALSE(mConfigurateState >= CONFIGURATE_FINISHED,
1008  NS_ERROR_ALREADY_INITIALIZED);
1009 
1010  nsresult rv;
1011 
1013  // no output set yet, do that now
1014  rv = DetermineOutputType();
1015  NS_ENSURE_SUCCESS(rv, rv);
1016  }
1017 
1018  nsCOMPtr<sbIMediaFormatAudio> audioFormat;
1019  rv = mInputFormat->GetAudioStream(getter_AddRefs(audioFormat));
1020  NS_ENSURE_SUCCESS(rv, rv);
1021 
1022  /* If we have an audio encoder selected, AND the input has audio, we can
1023  configure the audio details */
1024  if (audioFormat && !mAudioEncoder.IsEmpty()) {
1025  rv = SelectOutputAudioFormat();
1026  NS_ENSURE_SUCCESS(rv, rv);
1027 
1028  // Set the audio properties we want to use.
1029  rv = SetAudioProperties();
1030  NS_ENSURE_SUCCESS(rv, rv);
1031  }
1032  else {
1033  /* Otherwise, error out */
1034  LOG(("No audio, cannot configurate for audio-only"));
1035  return NS_ERROR_NOT_AVAILABLE;
1036  }
1037 
1038  /* Ensure video is disabled - this is an audio only configurator. */
1039  mVideoEncoder.SetIsVoid(PR_TRUE);
1040 
1041  // all done
1043  return NS_OK;
1044 }
readonly attribute AString audioEncoder
The audio encoder to use. The name is specific to the transcoder (for example, this may be a gstreame...
return NS_OK
inArray array
readonly attribute sbIMediaFormatAudio audioFormat
The basic audio format for data that is not specific to a particular codec.
nsresult CopyPropertiesIntoBag(nsIArray *aSrcProps, nsIWritablePropertyBag *aDstBag, PRBool aIsVideo)
const unsigned long TRANSCODE_TYPE_AUDIO
NS_IMPL_ISUPPORTS_INHERITED1(sbGStreamerTranscodeAudioConfigurator, sbTranscodingConfigurator, sbIDeviceTranscodingConfigurator)
An object defining a transcoding profile.
Songbird Variant Utility Definitions.
nsresult EnsureProfileAvailable(sbITranscodeProfile *aProfile)
nsDataHashtable< nsISupportsHashKey, EncoderProfileData > mElementNames
nsresult GetPreference(const nsAString &aPrefName, nsIVariant **_retval)
Definition: sbPrefBranch.h:170
const char * propName
GstCaps * GetCapsForMimeType(const nsACString &aMimeType, enum sbGstCapsMapType aType)
var count
Definition: test_bug7406.js:32
nsCOMPtr< sbIMediaFormat > mInputFormat
nsresult CheckProfileSupportedByDevice(sbITranscodeProfile *aProfile, sbIAudioFormatType **aFormat)
nsCOMPtr< nsIWritablePropertyBag2 > mAudioEncoderProperties
SimpleArrayEnumerator prototype hasMoreElements
StringArrayEnumerator prototype hasMore
var prefs
Definition: FeedWriter.js:1169
countRef value
Definition: FeedWriter.js:1423
long GetValue(in unsigned long aIndex)
var hidden
attribute sbIMediaFormat inputFormat
The input format to use when configuring the transcode profile.
#define min(a, b)
static nsresult MakeCapsFromAttributes(enum sbGstCapsMapType aType, const nsACString &aMimeType, nsIArray *aAttributes, GstCaps **aResultCaps)
nsCOMPtr< sbIMediaFormatAudio > mAudioFormat
#define SB_MEDIAFORMATAUDIO_CONTRACTID
_getSelectedPageStyle s i
const char * FindMatchingElementName(const char *srcCapsString, const char *typeName)
static nsresult GetDevCapRangeUpper(sbIDevCapRange *aRange, PRInt32 aTarget, PRInt32 *_retval)
var file
nsresult ApplyPreferencesToPropertyArray(sbIDevice *aDevice, nsIArray *aPropertyArray, nsString aPrefNameBase)