29 #include <sbIGStreamerService.h>
36 #include <nsServiceManagerUtils.h>
37 #include <nsThreadUtils.h>
38 #include <nsStringAPI.h>
39 #include <nsArrayUtils.h>
40 #include <nsNetUtil.h>
44 #include <nsIFileURL.h>
45 #include <nsIBinaryInputStream.h>
47 #include <gst/tag/tag.h>
51 #define PROGRESS_INTERVAL 200
58 static PRLogModuleInfo* gGStreamerTranscode = PR_NewLogModule(
"sbGStreamerTranscode");
59 #define LOG(args) PR_LOG(gGStreamerTranscode, PR_LOG_WARNING, args)
60 #define TRACE(args) PR_LOG(gGStreamerTranscode, PR_LOG_DEBUG, args)
87 sbGStreamerTranscode::sbGStreamerTranscode() :
93 sbGStreamerTranscode::~sbGStreamerTranscode()
100 sbGStreamerTranscode::Notify(nsITimer *aTimer)
102 NS_ENSURE_ARG_POINTER(aTimer);
112 sbGStreamerTranscode::SetSourceURI(
const nsAString& aSourceURI)
114 mSourceURI = aSourceURI;
122 sbGStreamerTranscode::GetSourceURI(nsAString& aSourceURI)
124 aSourceURI = mSourceURI;
129 sbGStreamerTranscode::SetDestURI(
const nsAString& aDestURI)
136 sbGStreamerTranscode::GetDestURI(nsAString& aDestURI)
145 NS_ENSURE_ARG_POINTER(aProfile);
154 NS_ENSURE_ARG_POINTER(aProfile);
156 NS_IF_ADDREF(*aProfile = mProfile);
163 NS_ENSURE_ARG_POINTER(aMetadata);
165 NS_IF_ADDREF(*aMetadata = mMetadata);
172 mMetadata = aMetadata;
177 sbGStreamerTranscode::GetMetadataImage(nsIInputStream **aImageStream)
179 NS_ENSURE_ARG_POINTER(aImageStream);
181 NS_IF_ADDREF(*aImageStream = mImageStream);
186 sbGStreamerTranscode::SetMetadataImage(nsIInputStream *aImageStream)
188 mImageStream = aImageStream;
195 nsCString pipelineString;
196 nsCString pipelineDescription;
197 GError *error = NULL;
198 GstElement *pipeline;
201 rv = BuildPipelineFragmentFromProfile(aProfile, pipelineDescription);
202 NS_ENSURE_SUCCESS (rv, NULL);
204 rv = BuildPipelineString(pipelineDescription, pipelineString);
205 NS_ENSURE_SUCCESS (rv, NULL);
210 pipeline = gst_parse_launch (pipelineString.BeginReading(), &error);
216 sbGStreamerTranscode::AddImageToTagList(GstTagList *aTags,
217 nsIInputStream *aStream)
219 PRUint32 imageDataLen;
223 nsCOMPtr<nsIBinaryInputStream> stream =
224 do_CreateInstance(
"@mozilla.org/binaryinputstream;1", &rv);
225 NS_ENSURE_SUCCESS(rv, rv);
227 rv = stream->SetInputStream(aStream);
228 NS_ENSURE_SUCCESS(rv, rv);
230 rv = aStream->Available(&imageDataLen);
231 NS_ENSURE_SUCCESS(rv, rv);
233 rv = stream->ReadByteArray(imageDataLen, &imageData);
234 NS_ENSURE_SUCCESS(rv, rv);
238 GstBuffer *imagebuf = gst_tag_image_data_to_image_buffer (
239 imageData, imageDataLen, GST_TAG_IMAGE_TYPE_FRONT_COVER);
241 return NS_ERROR_FAILURE;
243 gst_tag_list_add (aTags, GST_TAG_MERGE_REPLACE, GST_TAG_IMAGE,
245 gst_buffer_unref (imagebuf);
253 NS_ENSURE_STATE (mProfile);
258 mPipeline = BuildTranscodePipeline(mProfile);
263 rv = NS_ERROR_FAILURE;
274 AddImageToTagList (tags, mImageStream);
279 GstIterator *it = gst_bin_iterate_all_by_interface (
280 (GstBin *)
mPipeline, GST_TYPE_TAG_SETTER);
283 while (gst_iterator_next (it, (
void **)&element) == GST_ITERATOR_OK) {
284 GstTagSetter *setter = GST_TAG_SETTER (element);
288 gst_tag_setter_merge_tags (setter, tags, GST_TAG_MERGE_REPLACE);
289 g_object_unref (element);
291 gst_iterator_free (it);
292 gst_tag_list_free (tags);
306 NS_ENSURE_ARG_POINTER(aVote);
308 GstElement *pipeline = BuildTranscodePipeline(aProfile);
315 gst_object_unref (pipeline);
323 sbGStreamerTranscode::Transcode()
334 rv = sbGStreamerPipeline::PlayPipeline();
335 NS_ENSURE_SUCCESS (rv, rv);
337 rv = StartProgressReporting();
338 NS_ENSURE_SUCCESS (rv, rv);
349 rv = sbGStreamerPipeline::StopPipeline();
350 NS_ENSURE_SUCCESS (rv, rv);
352 rv = StopProgressReporting();
353 NS_ENSURE_SUCCESS (rv, rv);
356 rv = OnJobProgress();
357 NS_ENSURE_SUCCESS (rv, rv);
365 sbGStreamerTranscode::GetCanCancel(PRBool *aCanCancel)
367 NS_ENSURE_ARG_POINTER(aCanCancel);
369 *aCanCancel = PR_TRUE;
374 sbGStreamerTranscode::Cancel()
379 NS_ENSURE_SUCCESS (rv, rv);
387 sbGStreamerTranscode::GetElapsedTime(PRUint32 *aElapsedTime)
389 NS_ENSURE_ARG_POINTER(aElapsedTime);
392 *aElapsedTime =
static_cast<PRUint32
>(
GetRunningTime() / GST_MSECOND);
398 sbGStreamerTranscode::GetRemainingTime(PRUint32 *aRemainingTime)
400 GstClockTime
duration = QueryDuration();
401 GstClockTime
position = QueryPosition();
404 if (duration == GST_CLOCK_TIME_NONE || position == GST_CLOCK_TIME_NONE ||
405 elapsed == GST_CLOCK_TIME_NONE)
408 *aRemainingTime = (PRUint32)-1;
411 GstClockTime totalTime = gst_util_uint64_scale (elapsed, duration,
415 static_cast<PRUint32
>((totalTime - elapsed) / GST_MSECOND);
422 sbGStreamerTranscode::GetStatus(PRUint16 *aStatus)
424 NS_ENSURE_ARG_POINTER(aStatus);
432 sbGStreamerTranscode::GetBlocked(PRBool *aBlocked)
434 NS_ENSURE_ARG_POINTER(aBlocked);
436 *aBlocked = PR_FALSE;
442 sbGStreamerTranscode::GetStatusText(nsAString& aText)
444 nsresult rv = NS_ERROR_FAILURE;
449 NS_LITERAL_STRING(
"mediacore.gstreamer.transcode.failed"));
453 NS_LITERAL_STRING(
"mediacore.gstreamer.transcode.succeeded"));
457 NS_LITERAL_STRING(
"mediacore.gstreamer.transcode.running"));
460 NS_NOTREACHED(
"Status is invalid");
467 sbGStreamerTranscode::GetTitleText(nsAString& aText)
470 NS_LITERAL_STRING(
"mediacore.gstreamer.transcode.title"));
474 sbGStreamerTranscode::GetProgress(PRUint32* aProgress)
476 NS_ENSURE_ARG_POINTER(aProgress);
478 GstClockTime duration = QueryDuration();
479 GstClockTime position = QueryPosition();
481 if (duration != GST_CLOCK_TIME_NONE && position != GST_CLOCK_TIME_NONE &&
483 *aProgress = (PRUint32)gst_util_uint64_scale (position, 1000, duration);
491 sbGStreamerTranscode::GetTotal(PRUint32* aTotal)
493 NS_ENSURE_ARG_POINTER(aTotal);
495 GstClockTime duration = QueryDuration();
500 if (duration != GST_CLOCK_TIME_NONE)
511 sbGStreamerTranscode::GetErrorCount(PRUint32* aErrorCount)
513 NS_ENSURE_ARG_POINTER(aErrorCount);
514 NS_ASSERTION(NS_IsMainThread(),
515 "sbIJobProgress::GetErrorCount is main thread only!");
517 *aErrorCount = mErrorMessages.Length();
525 NS_ENSURE_ARG_POINTER(aMessages);
526 NS_ASSERTION(NS_IsMainThread(),
527 "sbIJobProgress::GetProgress is main thread only!");
531 nsCOMPtr<nsIStringEnumerator> enumerator =
533 NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
535 enumerator.forget(aMessages);
542 NS_ENSURE_ARG_POINTER(aListener);
543 NS_ASSERTION(NS_IsMainThread(), \
544 "sbGStreamerTranscode::AddJobProgressListener is main thread only!");
546 PRInt32 index = mProgressListeners.IndexOf(aListener);
549 return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
551 PRBool
succeeded = mProgressListeners.AppendObject(aListener);
552 NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
558 sbGStreamerTranscode::RemoveJobProgressListener(
561 NS_ENSURE_ARG_POINTER(aListener);
562 NS_ASSERTION(NS_IsMainThread(), \
563 "sbGStreamerTranscode::RemoveJobProgressListener is main thread only!");
565 PRInt32 indexToRemove = mProgressListeners.IndexOf(aListener);
566 if (indexToRemove < 0) {
572 PRBool succeeded = mProgressListeners.RemoveObjectAt(indexToRemove);
573 NS_ENSURE_TRUE(succeeded, NS_ERROR_FAILURE);
580 sbGStreamerTranscode::OnJobProgress()
582 TRACE((
"sbGStreamerTranscode::OnJobProgress[0x%.8x]",
this));
583 NS_ASSERTION(NS_IsMainThread(), \
584 "sbGStreamerTranscode::OnJobProgress is main thread only!");
587 for (PRInt32
i = mProgressListeners.Count() - 1;
i >= 0; --
i) {
589 mProgressListeners[
i]->OnJobProgress(
this);
594 void sbGStreamerTranscode::HandleErrorMessage(GstMessage *
message)
596 GError *gerror = NULL;
601 gst_message_parse_error(message, &gerror, &debug);
603 mErrorMessages.AppendElement(
604 NS_ConvertUTF8toUTF16(nsDependentCString(gerror->message)));
606 g_error_free (gerror);
613 void sbGStreamerTranscode::HandleEOSMessage(GstMessage *message)
615 TRACE((
"sbGStreamerTranscode::HandleEOSMessage[0x%.8x]",
this));
623 GstClockTime sbGStreamerTranscode::QueryPosition()
626 gint64 position = GST_CLOCK_TIME_NONE;
631 query = gst_query_new_position(GST_FORMAT_TIME);
634 gst_query_parse_position(query, NULL, &position);
636 gst_query_unref (query);
641 GstClockTime sbGStreamerTranscode::QueryDuration()
644 gint64 duration = GST_CLOCK_TIME_NONE;
649 query = gst_query_new_duration(GST_FORMAT_TIME);
652 gst_query_parse_duration(query, NULL, &duration);
654 gst_query_unref (query);
660 sbGStreamerTranscode::StartProgressReporting()
662 NS_ENSURE_STATE(!mProgressTimer);
663 TRACE((
"sbGStreamerTranscode::StartProgressReporting[0x%.8x]",
this));
667 do_CreateInstance(
"@mozilla.org/timer;1", &rv);
668 NS_ENSURE_SUCCESS(rv, rv);
670 mProgressTimer->InitWithCallback(
this,
677 sbGStreamerTranscode::StopProgressReporting()
679 TRACE((
"sbGStreamerTranscode::StopProgressReporting[0x%.8x]",
this));
681 if (mProgressTimer) {
682 mProgressTimer->Cancel();
683 mProgressTimer = nsnull;
690 sbGStreamerTranscode::BuildPipelineString(nsCString pipelineDescription,
691 nsACString &pipeline)
703 pipeline.Append(NS_ConvertUTF16toUTF8(mSourceURI));
704 pipeline.AppendLiteral(
" ! decodebin ! audioconvert ! audioresample ! ");
705 pipeline.Append(pipelineDescription);
706 pipeline.AppendLiteral(
" ! ");
707 pipeline.Append(NS_ConvertUTF16toUTF8(mDestURI));
709 LOG((
"Built pipeline string: '%s'", pipeline.BeginReading()));
715 sbGStreamerTranscode::BuildPipelineFragmentFromProfile(
718 NS_ENSURE_ARG_POINTER(aProfile);
724 nsCString gstContainerMuxer;
725 nsCString gstAudioEncoder;
726 nsCOMPtr<nsIArray> containerProperties;
727 nsCOMPtr<nsIArray> audioProperties;
729 rv = aProfile->GetType(&type);
730 NS_ENSURE_SUCCESS (rv, rv);
732 rv = aProfile->GetContainerFormat(container);
733 NS_ENSURE_SUCCESS (rv, rv);
735 rv = aProfile->GetContainerProperties(getter_AddRefs(containerProperties));
736 NS_ENSURE_SUCCESS (rv, rv);
738 rv = aProfile->GetAudioCodec(audioCodec);
739 NS_ENSURE_SUCCESS (rv, rv);
741 rv = aProfile->GetAudioProperties(getter_AddRefs(audioProperties));
742 NS_ENSURE_SUCCESS (rv, rv);
745 return NS_ERROR_FAILURE;
748 if (!audioCodec.IsEmpty()) {
749 rv = GetAudioCodec(audioCodec, audioProperties, gstAudioEncoder);
750 NS_ENSURE_SUCCESS (rv, rv);
752 pipelineFragment.Append(gstAudioEncoder);
755 if (!container.IsEmpty()) {
756 rv = GetContainer(container, containerProperties, gstContainerMuxer);
757 NS_ENSURE_SUCCESS (rv, rv);
759 pipelineFragment.AppendLiteral(
" ! ");
760 pipelineFragment.Append(gstContainerMuxer);
767 sbGStreamerTranscode::EstimateOutputSize(PRInt32 inputDuration,
785 NS_ENSURE_STATE (mProfile);
786 NS_ENSURE_ARG_POINTER(_retval);
788 PRUint32 bitrate = 0;
790 nsCOMPtr<nsIArray> audioProperties;
792 rv = mProfile->GetAudioProperties(getter_AddRefs(audioProperties));
793 NS_ENSURE_SUCCESS (rv, rv);
795 PRUint32 propertiesLength = 0;
796 rv = audioProperties->GetLength(&propertiesLength);
797 NS_ENSURE_SUCCESS (rv, rv);
799 for (PRUint32 j = 0; j < propertiesLength; j++) {
800 nsCOMPtr<sbITranscodeProfileProperty>
property =
801 do_QueryElementAt(audioProperties, j, &rv);
802 NS_ENSURE_SUCCESS(rv, rv);
805 rv =
property->GetPropertyName(propName);
806 NS_ENSURE_SUCCESS(rv, rv);
808 if (propName.EqualsLiteral(
"bitrate")) {
809 nsCOMPtr<nsIVariant> propValue;
810 rv =
property->GetValue(getter_AddRefs(propValue));
811 NS_ENSURE_SUCCESS(rv, rv);
814 rv = propValue->GetAsUint32(&bitrate);
815 NS_ENSURE_SUCCESS(rv, rv);
824 bitrate =
static_cast<PRUint32
>(0.6 * (44100 * 2 * 16));
828 PRUint64 size = (PRUint64)inputDuration * bitrate / 8 / 1000;
844 {
"application/ogg",
"application/ogg"},
845 {
"audio/mpeg",
"application/x-id3"},
846 {
"video/x-ms-asf",
"video/x-ms-asf"},
847 {
"audio/x-wav",
"audio/x-wav"},
848 {
"video/mp4",
"video/quicktime, variant=iso"},
849 {
"video/3gpp",
"video/quicktime, variant=(string)3gpp"}
853 sbGStreamerTranscode::GetContainer(nsAString &container, nsIArray *properties,
854 nsACString &gstMuxer)
856 nsCString cont = NS_ConvertUTF16toUTF8 (container);
858 for (
unsigned int i = 0;
862 if (strcmp (cont.BeginReading(), SupportedContainers[
i].
name) == 0)
864 const char *capsString = SupportedContainers[
i].
gstCapsName;
866 capsString,
"Muxer");
867 if (!gstElementName) {
876 gstMuxer.Append(gstElementName);
882 return NS_ERROR_FAILURE;
886 {
"audio/x-vorbis",
"audio/x-vorbis"},
887 {
"audio/x-flac",
"audio/x-flac"},
888 {
"audio/x-ms-wma",
"audio/x-wma, wmaversion=(int)2"},
889 {
"audio/mpeg",
"audio/mpeg, mpegversion=(int)1, layer=(int)3"},
890 {
"audio/aac",
"audio/mpeg, mpegversion=(int)4"},
894 sbGStreamerTranscode::GetAudioCodec(nsAString &aCodec, nsIArray *properties,
895 nsACString &gstCodec)
898 nsCString codec = NS_ConvertUTF16toUTF8 (aCodec);
900 for (
unsigned int i = 0;
904 if (strcmp (codec.BeginReading(), SupportedAudioCodecs[
i].
name) == 0)
906 const char *capsString = SupportedAudioCodecs[
i].
gstCapsName;
908 capsString,
"Encoder");
912 gstCodec.Append(gstElementName);
915 PRUint32 propertiesLength = 0;
916 rv = properties->GetLength(&propertiesLength);
917 NS_ENSURE_SUCCESS (rv, rv);
919 for (PRUint32 j = 0; j < propertiesLength; j++) {
920 nsCOMPtr<sbITranscodeProfileProperty>
property =
921 do_QueryElementAt(properties, j, &rv);
922 NS_ENSURE_SUCCESS(rv, rv);
925 rv =
property->GetPropertyName(propName);
926 NS_ENSURE_SUCCESS(rv, rv);
928 nsCOMPtr<nsIVariant> propValue;
929 rv =
property->GetValue(getter_AddRefs(propValue));
930 NS_ENSURE_SUCCESS(rv, rv);
932 nsString propValueString;
933 rv = propValue->GetAsAString(propValueString);
934 NS_ENSURE_SUCCESS(rv, rv);
939 gstCodec.AppendLiteral(
" ");
940 gstCodec.Append(NS_ConvertUTF16toUTF8(propName));
941 gstCodec.AppendLiteral(
"=");
942 gstCodec.Append(NS_ConvertUTF16toUTF8(propValueString));
950 return NS_ERROR_FAILURE;
954 sbGStreamerTranscode::GetAvailableProfiles(nsIArray * *aAvailableProfiles)
956 if (mAvailableProfiles) {
957 NS_IF_ADDREF (*aAvailableProfiles = mAvailableProfiles);
965 nsCOMPtr<nsISimpleEnumerator> dirEnum;
967 nsCOMPtr<nsIURI> profilesDirURI;
968 rv = NS_NewURI(getter_AddRefs(profilesDirURI),
969 NS_LITERAL_STRING(
"resource://app/gstreamer/encode-profiles"));
970 NS_ENSURE_SUCCESS(rv, rv);
972 nsCOMPtr<nsIFileURL> profilesDirFileURL =
973 do_QueryInterface(profilesDirURI, &rv);
974 NS_ENSURE_SUCCESS(rv, rv);
976 nsCOMPtr<nsIFile> profilesDir;
977 rv = profilesDirFileURL->GetFile(getter_AddRefs(profilesDir));
978 NS_ENSURE_SUCCESS(rv, rv);
980 nsCOMPtr<nsIMutableArray>
array =
981 do_CreateInstance(
"@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
982 NS_ENSURE_SUCCESS(rv, rv);
984 nsCOMPtr<sbITranscodeProfileLoader> profileLoader =
985 do_CreateInstance(
"@songbirdnest.com/Songbird/Transcode/ProfileLoader;1",
987 NS_ENSURE_SUCCESS (rv, rv);
989 rv = profilesDir->GetDirectoryEntries(getter_AddRefs(dirEnum));
990 NS_ENSURE_SUCCESS (rv, rv);
993 rv = dirEnum->HasMoreElements(&hasMoreElements);
994 NS_ENSURE_SUCCESS(rv, rv);
995 if (!hasMoreElements)
998 nsCOMPtr<nsIFile>
file;
999 rv = dirEnum->GetNext(getter_AddRefs(file));
1000 NS_ENSURE_SUCCESS(rv, rv);
1002 nsCOMPtr<sbITranscodeProfile>
profile;
1004 rv = profileLoader->LoadProfile(file, getter_AddRefs(profile));
1008 GstElement *pipeline = BuildTranscodePipeline(profile);
1015 gst_object_unref (pipeline);
1017 rv = array->AppendElement(profile, PR_FALSE);
1018 NS_ENSURE_SUCCESS (rv, rv);
1021 mAvailableProfiles = do_QueryInterface(array, &rv);
1022 NS_ENSURE_SUCCESS (rv, rv);
1024 NS_ADDREF(*aAvailableProfiles = mAvailableProfiles);
NS_IMETHOD StopPipeline()
function succeeded(ch, cx, status, data)
Generic interface for exposing long running jobs to the UI.
static struct GSTNameMap SupportedAudioCodecs[]
NS_INTERFACE_MAP_END NS_IMPL_CI_INTERFACE_GETTER6(sbDeviceLibrary, nsIClassInfo, sbIDeviceLibrary, sbILibrary, sbIMediaList, sbIMediaItem, sbILibraryResource) sbDeviceLibrary
const unsigned long TRANSCODE_TYPE_AUDIO
An object defining a transcoding profile.
const unsigned short STATUS_SUCCEEDED
Constant indicating that the job has completed.
virtual nsresult BuildPipeline()
Generic interface extending sbIJobProgress that can track expected time, etc in addition to abstract ...
GstClockTime GetRunningTime()
virtual void HandleErrorMessage(GstMessage *message)
attribute sbITranscodeProfile profile
The encoding profile to use.
static struct GSTNameMap SupportedContainers[]
const unsigned short STATUS_RUNNING
Constant indicating that the job is active.
NS_IMPL_THREADSAFE_CI(sbGStreamerTranscode)
sbIJobCancelable NS_DECL_CLASSINFO(sbGStreamerTranscode)
virtual void HandleEOSMessage(GstMessage *message)
nsresult SBGetLocalizedString(nsAString &aString, const nsAString &aKey, const nsAString &aDefault, class nsIStringBundle *aStringBundle)
NS_IMPL_THREADSAFE_ISUPPORTS8(sbGStreamerTranscode, nsIClassInfo, sbIGStreamerPipeline, sbITranscodeJob, sbIMediacoreEventTarget, sbIJobProgress, sbIJobProgressTime, sbIJobCancelable, nsITimerCallback) NS_IMPL_CI_INTERFACE_GETTER6(sbGStreamerTranscode
SimpleArrayEnumerator prototype hasMoreElements
NS_IMETHOD PlayPipeline()
An object capable of transcoding a source URI to a destination file.
nsString mResourceDisplayName
void SetPipelineOp(GStreamer::pipelineOp_t aPipelineOp)
Implemented to receive notifications from sbIJobProgress interfaces.
#define PROGRESS_INTERVAL
const unsigned short STATUS_FAILED
Constant indicating that the job has completed with errors.
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
_getSelectedPageStyle s i