Audacity 3.2.0
Public Member Functions | Private Attributes | List of all members
GStreamerImportFileHandle Class Referencefinal

! Does actual import, returned by GStreamerImportPlugin::Open More...

Inheritance diagram for GStreamerImportFileHandle:
[legend]
Collaboration diagram for GStreamerImportFileHandle:
[legend]

Public Member Functions

 GStreamerImportFileHandle (const wxString &name)
 
virtual ~GStreamerImportFileHandle ()
 
bool Init ()
 
TranslatableString GetFileDescription () override
 
ByteCount GetFileUncompressedBytes () override
 
wxInt32 GetStreamCount () override
 
const TranslatableStringsGetStreamInfo () override
 
void SetStreamUsage (wxInt32 index, bool use) override
 
int Import (TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
 
void OnPadAdded (GstPad *pad)
 
void OnPadRemoved (GstPad *pad)
 
bool ProcessBusMessage (bool &success)
 
void OnTag (GstAppSink *appsink, GstTagList *tags)
 
void OnNewSample (GStreamContext *c, GstSample *sample)
 
- Public Member Functions inherited from ImportFileHandle
 ImportFileHandle (const FilePath &filename)
 
virtual ~ImportFileHandle ()
 
void CreateProgress ()
 
virtual TranslatableString GetFileDescription ()=0
 
virtual ByteCount GetFileUncompressedBytes ()=0
 
virtual wxInt32 GetStreamCount ()=0
 
virtual const TranslatableStringsGetStreamInfo ()=0
 
virtual void SetStreamUsage (wxInt32 StreamID, bool Use)=0
 
virtual ProgressResult Import (WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags)=0
 

Private Attributes

TranslatableStrings mStreamInfo
 Array of stream descriptions. Length is the same as mStreams. More...
 
Tags mTags
 Tags to be passed back to Audacity. More...
 
TrackFactory * mTrackFactory
 Factory to create tracks when samples arrive. More...
 
GstString mUri
 URI of file. More...
 
GstObjHandle< GstElement > mPipeline
 GStreamer pipeline. More...
 
GstObjHandle< GstBus > mBus
 Message bus. More...
 
GstElement * mDec
 uridecodebin element More...
 
bool mAsyncDone
 true = 1st async-done message received More...
 
GMutex mStreamsLock
 Mutex protecting the mStreams array. More...
 
std::vector< std::unique_ptr< GStreamContext > > mStreams
 Array of pointers to stream contexts. More...
 

Additional Inherited Members

- Public Types inherited from ImportFileHandle
using ProgressResult = BasicUI::ProgressResult
 
using ByteCount = unsigned long long
 
- Static Public Member Functions inherited from ImportFileHandle
static sampleFormat ChooseFormat (sampleFormat effectiveFormat)
 Choose appropriate format, which will not be narrower than the specified one. More...
 
- Protected Member Functions inherited from ImportFileHandle
std::shared_ptr< WaveTrackNewWaveTrack (WaveTrackFactory &trackFactory, sampleFormat effectiveFormat, double rate)
 Build a wave track with appropriate format, which will not be narrower than the specified one. More...
 
- Protected Attributes inherited from ImportFileHandle
FilePath mFilename
 
std::unique_ptr< ProgressDialogmProgress
 

Detailed Description

! Does actual import, returned by GStreamerImportPlugin::Open

An ImportFileHandle for GStreamer data.

Definition at line 158 of file ImportGStreamer.cpp.

Constructor & Destructor Documentation

◆ GStreamerImportFileHandle()

GStreamerImportFileHandle::GStreamerImportFileHandle ( const wxString &  name)

Definition at line 839 of file ImportGStreamer.cpp.

841{
842 mDec = NULL;
843 mTrackFactory = NULL;
844 mAsyncDone = false;
845
846 g_mutex_init(&mStreamsLock);
847}
const TranslatableString name
Definition: Distortion.cpp:76
GMutex mStreamsLock
Mutex protecting the mStreams array.
bool mAsyncDone
true = 1st async-done message received
GstElement * mDec
uridecodebin element
TrackFactory * mTrackFactory
Factory to create tracks when samples arrive.
ImportFileHandle(const FilePath &filename)

References mAsyncDone, mDec, mStreamsLock, and mTrackFactory.

◆ ~GStreamerImportFileHandle()

GStreamerImportFileHandle::~GStreamerImportFileHandle ( )
virtual

Definition at line 851 of file ImportGStreamer.cpp.

852{
853 // Make sure the pipeline isn't running
854 if (mPipeline)
855 {
856 gst_element_set_state(mPipeline.get(), GST_STATE_NULL);
857 }
858
859 // Delete all of the contexts
860 if (mStreams.size())
861 {
862 // PRL: is the FIFO destruction order important?
863 // If not, then you could simply clear mStreams.
864
865 {
867 while (mStreams.size() > 0)
868 {
869 // remove context from the array
870 mStreams.erase(mStreams.begin());
871 }
872 }
873
874 // Done with the context array
875 }
876
877 // Release the decoder
878 if (mDec != NULL)
879 {
880 gst_bin_remove(GST_BIN(mPipeline.get()), mDec);
881 }
882
883 g_mutex_clear(&mStreamsLock);
884}
GstObjHandle< GstElement > mPipeline
GStreamer pipeline.
std::vector< std::unique_ptr< GStreamContext > > mStreams
Array of pointers to stream contexts.

References mDec, mPipeline, mStreams, and mStreamsLock.

Member Function Documentation

◆ GetFileDescription()

TranslatableString GStreamerImportFileHandle::GetFileDescription ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 1001 of file ImportGStreamer.cpp.

1002{
1003 return DESC;
1004}
#define DESC

References DESC.

◆ GetFileUncompressedBytes()

auto GStreamerImportFileHandle::GetFileUncompressedBytes ( )
overridevirtual

Implements ImportFileHandle.

Definition at line 1009 of file ImportGStreamer.cpp.

1010{
1011 return 0;
1012}

◆ GetStreamCount()

wxInt32 GStreamerImportFileHandle::GetStreamCount ( )
overridevirtual

! Called by Import.cpp

Returns
number of readable audio streams in the file

Implements ImportFileHandle.

Definition at line 889 of file ImportGStreamer.cpp.

890{
891 return mStreamInfo.size();
892}
TranslatableStrings mStreamInfo
Array of stream descriptions. Length is the same as mStreams.

References mStreamInfo.

◆ GetStreamInfo()

const TranslatableStrings & GStreamerImportFileHandle::GetStreamInfo ( )
overridevirtual

! Called by Import.cpp

Returns
array of strings - descriptions of the streams

Implements ImportFileHandle.

Definition at line 897 of file ImportGStreamer.cpp.

898{
899 return mStreamInfo;
900}

References mStreamInfo.

◆ Import()

int GStreamerImportFileHandle::Import ( TrackFactory *  trackFactory,
TrackHolders outTracks,
Tags tags 
)
override

! Imports audio

Returns
import status (see Import.cpp)

Definition at line 1017 of file ImportGStreamer.cpp.

1020{
1021 outTracks.clear();
1022
1023 // Save track factory pointer
1024 mTrackFactory = trackFactory;
1025
1026 // Create the progress dialog
1028
1029 // Block streams that are to be bypassed
1030 bool haveStreams = false;
1031 {
1032 g_mutex_locker locker{ mStreamsLock };
1033 for (guint i = 0; i < mStreams.size(); i++)
1034 {
1035 GStreamContext *c = mStreams[i].get();
1036
1037 // Did the user choose to skip this stream?
1038 if (!c->mUse)
1039 {
1040 // Get the audioconvert sink pad and unlink
1041 {
1042 GstObjHandle<GstPad> convsink{ gst_element_get_static_pad(c->mConv, "sink") };
1043
1044 {
1045 GstObjHandle<GstPad> convpeer{ gst_pad_get_peer(convsink.get()) };
1046 gst_pad_unlink(convpeer.get(), convsink.get());
1047 }
1048
1049 // Set bitbucket callbacks so the prerolled sample won't get processed
1050 // when we change the state to PLAYING
1051 gst_app_sink_set_callbacks(GST_APP_SINK(c->mSink), &AppSinkBitBucket, this, NULL);
1052
1053 // Set state to playing for conv and sink so EOS gets processed
1054 gst_element_set_state(c->mConv, GST_STATE_PLAYING);
1055 gst_element_set_state(c->mSink, GST_STATE_PLAYING);
1056
1057 // Send an EOS event to the pad to force them to drain
1058 gst_pad_send_event(convsink.get(), gst_event_new_eos());
1059
1060 // Resync state with pipeline
1061 gst_element_sync_state_with_parent(c->mConv);
1062 gst_element_sync_state_with_parent(c->mSink);
1063
1064 // Done with the pad
1065 }
1066
1067 // Unlink audioconvert and appsink
1068 gst_element_unlink(c->mConv, c->mSink);
1069
1070 // Remove them from the bin
1071 gst_bin_remove_many(GST_BIN(mPipeline.get()), c->mConv, c->mSink, NULL);
1072
1073 // All done with them
1074 c->mConv = NULL;
1075 c->mSink = NULL;
1076
1077 continue;
1078 }
1079
1080 // We have a stream to process
1081 haveStreams = true;
1082 }
1083 }
1084
1085 // Can't do much if we don't have any streams to process
1086 if (!haveStreams)
1087 {
1089 XO("File doesn't contain any audio streams."),
1090 XO("GStreamer Importer"));
1091 return ProgressResult::Failed;
1092 }
1093
1094 // Get the ball rolling...
1095 GstStateChangeReturn state = gst_element_set_state(mPipeline.get(), GST_STATE_PLAYING);
1096 if (state == GST_STATE_CHANGE_FAILURE)
1097 {
1099 XO("Unable to import file, state change failed."),
1100 XO("GStreamer Importer"));
1101 return ProgressResult::Failed;
1102 }
1103
1104 // Get the duration of the stream
1105 gint64 duration;
1106 gst_element_query_duration(mPipeline.get(), GST_FORMAT_TIME, &duration);
1107
1108 // Handle bus messages and update progress while files is importing
1109 bool success = true;
1110 int updateResult = ProgressResult::Success;
1111 while (ProcessBusMessage(success) && success && updateResult == ProgressResult::Success)
1112 {
1113 gint64 position;
1114
1115 // Update progress indicator and give user chance to abort
1116 if (gst_element_query_position(mPipeline.get(), GST_FORMAT_TIME, &position))
1117 {
1118 updateResult = mProgress->Update((wxLongLong_t) position,
1119 (wxLongLong_t) duration);
1120 }
1121 }
1122
1123 // Disable pipeline
1124 gst_element_set_state(mPipeline.get(), GST_STATE_NULL);
1125
1126 // Something bad happened
1127 if (!success || updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled)
1128 {
1129 return updateResult;
1130 }
1131
1132 // Grab the streams lock
1133 g_mutex_locker locker{ mStreamsLock };
1134
1135 // Copy audio from mChannels to newly created tracks (destroying mChannels in process)
1136 int trackindex = 0;
1137 for (guint s = 0; s < mStreams.size(); s++)
1138 {
1139 GStreamContext *c = mStreams[s].get();
1140 if (c->mNumChannels)
1141 {
1142 for (int ch = 0; ch < c->mNumChannels; ch++)
1143 c->mChannels[ch]->Flush();
1144 outTracks.push_back(std::move(c->mChannels));
1145 }
1146 }
1147
1148 // Set any tags found in the stream
1149 *tags = mTags;
1150
1151 return updateResult;
1152}
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
XO("Cut/Copy/Paste")
std::unique_ptr< T, Deleter< void, gst_object_unref > > GstObjHandle
static GstAppSinkCallbacks AppSinkBitBucket
Tags mTags
Tags to be passed back to Audacity.
bool ProcessBusMessage(bool &success)
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:164
NewChannelGroup mChannels
GstElement * mSink
GstElement * mConv

References AppSinkBitBucket, AudacityMessageBox(), RefreshCode::Cancelled, ImportFileHandle::CreateProgress(), GStreamContext::mChannels, GStreamContext::mConv, GStreamContext::mNumChannels, mPipeline, ImportFileHandle::mProgress, GStreamContext::mSink, mStreams, mStreamsLock, mTags, mTrackFactory, GStreamContext::mUse, ProcessBusMessage(), BasicUI::Success, and XO().

Here is the call graph for this function:

◆ Init()

bool GStreamerImportFileHandle::Init ( )

! Format initialization

Returns
true if successful, false otherwise

Definition at line 918 of file ImportGStreamer.cpp.

919{
920 // Create a URI from the filename
921 mUri.reset(g_strdup_printf("file:///%s", mFilename.ToUTF8().data()));
922 if (!mUri)
923 {
924 wxLogMessage(wxT("GStreamerImport couldn't create URI"));
925 return false;
926 }
927
928 // Create a pipeline
929 mPipeline.reset(gst_pipeline_new("pipeline"));
930
931 // Get its bus
932 mBus.reset(gst_pipeline_get_bus(GST_PIPELINE(mPipeline.get())));
933
934 // Create uridecodebin and set up signal handlers
935 mDec = gst_element_factory_make("uridecodebin", "decoder");
936 g_signal_connect(mDec, "autoplug-select", G_CALLBACK(GStreamerAutoplugSelectCallback), (gpointer) this);
937 g_signal_connect(mDec, "pad-added", G_CALLBACK(GStreamerPadAddedCallback), (gpointer) this);
938 g_signal_connect(mDec, "pad-removed", G_CALLBACK(GStreamerPadRemovedCallback), (gpointer) this);
939
940 // Set the URI
941 g_object_set(G_OBJECT(mDec), "uri", mUri, NULL);
942
943 // Add the decoder to the pipeline
944 if (!gst_bin_add(GST_BIN(mPipeline.get()), mDec))
945 {
947 XO("Unable to add decoder to pipeline"),
948 XO("GStreamer Importer"));
949
950 // Cleanup expected to occur in destructor
951 return false;
952 }
953
954 // Run the pipeline
955 GstStateChangeReturn state = gst_element_set_state(mPipeline.get(), GST_STATE_PAUSED);
956 if (state == GST_STATE_CHANGE_FAILURE)
957 {
959 XO("Unable to set stream state to paused."),
960 XO("GStreamer Importer"));
961 return false;
962 }
963
964 // Collect info while the stream is prerolled
965 //
966 // Unfortunately, for some files this may cause a slight "pause" in the GUI
967 // without a progress dialog appearing. Not much can be done about it other
968 // than throwing up an additional progress dialog and displaying two dialogs
969 // may be confusing to the users.
970
971 // Process messages until we get an error or the ASYNC_DONE message is received
972 bool success;
973 while (ProcessBusMessage(success) && success)
974 {
975 // Give wxWidgets a chance to do housekeeping
976 wxSafeYield();
977 }
978
979 // Build the stream info array
981 for (guint i = 0; i < mStreams.size(); i++)
982 {
983 GStreamContext *c = mStreams[i].get();
984
985 // Create stream info string
986 auto strinfo = XO("Index[%02d], Type[%s], Channels[%d], Rate[%d]")
987 .Format(
988 (unsigned int) i,
989 wxString::FromUTF8(c->mType.get()),
990 (int) c->mNumChannels,
991 (int) c->mSampleRate );
992 mStreamInfo.push_back(strinfo);
993 }
994
995 return success;
996}
wxT("CloseDown"))
static void GStreamerPadRemovedCallback(GstElement *WXUNUSED(element), GstPad *pad, gpointer data)
static void GStreamerPadAddedCallback(GstElement *WXUNUSED(element), GstPad *pad, gpointer data)
static gint GStreamerAutoplugSelectCallback(GstElement *WXUNUSED(element), GstPad *WXUNUSED(pad), GstCaps *WXUNUSED(caps), GstElementFactory *factory, gpointer WXUNUSED(data))
GstString mUri
URI of file.
GstObjHandle< GstBus > mBus
Message bus.
FilePath mFilename
Definition: ImportPlugin.h:163

References AudacityMessageBox(), GStreamerAutoplugSelectCallback(), GStreamerPadAddedCallback(), GStreamerPadRemovedCallback(), mBus, mDec, ImportFileHandle::mFilename, GStreamContext::mNumChannels, mPipeline, GStreamContext::mSampleRate, mStreamInfo, mStreams, mStreamsLock, GStreamContext::mType, mUri, ProcessBusMessage(), wxT(), and XO().

Here is the call graph for this function:

◆ OnNewSample()

void GStreamerImportFileHandle::OnNewSample ( GStreamContext c,
GstSample *  sample 
)

! Called when a NEW samples are queued

Parameters
c- stream context
sample- gstreamer sample

Definition at line 730 of file ImportGStreamer.cpp.

731{
732 // Allocate NEW tracks
733 //
734 // It is done here because, at least in the case of chained oggs,
735 // not all streams are known ahead of time.
736 if (c->mChannels.empty())
737 {
738 // Get the sample format...no need to release caps or structure
739 GstCaps *caps = gst_sample_get_caps(sample);
740 GstStructure *str = gst_caps_get_structure(caps, 0);
741 const gchar *fmt = gst_structure_get_string(str, "format");
742 if (!fmt)
743 {
744 WARN(mPipeline.get(), ("OnNewSample: missing audio format"));
745 return;
746 }
747
748 // Determinate sample format based on negotiated format
749 if (strcmp(fmt, GST_AUDIO_NE(S16)) == 0)
750 {
751 c->mFmt = int16Sample;
752 }
753 else if (strcmp(fmt, GST_AUDIO_NE(S24_32)) == 0)
754 {
755 c->mFmt = int24Sample;
756 }
757 else if (strcmp(fmt, GST_AUDIO_NE(F32)) == 0)
758 {
759 c->mFmt = floatSample;
760 }
761 else
762 {
763 // This shouldn't really happen since audioconvert will only give us
764 // the formats we said we could handle.
765 WARN(mPipeline.get(), ("OnNewSample: unrecognized sample format %s", fmt));
766 return;
767 }
768
769 // Allocate the track array
770 c->mChannels.resize(c->mNumChannels);
771 if (c->mChannels.size() != c->mNumChannels)
772 {
773 WARN(mPipeline.get(), ("OnNewSample: unable to allocate track array"));
774 return;
775 }
776
777 // Allocate all channels
778 for (int ch = 0; ch < c->mNumChannels; ch++)
779 {
780 // Create a track
781 c->mChannels[ch] = mTrackFactory->NewWaveTrack(c->mFmt, c->mSampleRate);
782 if (!c->mChannels[ch])
783 {
784 WARN(mPipeline.get(), ("OnNewSample: unable to create track"));
785 return;
786 }
787 }
788 }
789
790 // Get the buffer for the sample...no need to release
791 GstBuffer *buffer = gst_sample_get_buffer(sample);
792 if (!buffer)
793 {
794 // No buffer...not sure if this is an error or not,
795 // but we can't do anything else, so just bail silently.
796 return;
797 }
798
799 // Map the buffer
800 GstMapInfo info;
801 if (!gst_buffer_map(buffer, &info, GST_MAP_READ))
802 {
803 WARN(mPipeline.get(), ("OnNewSample: mapping buffer failed"));
804 return;
805 }
806 auto cleanup = finally([&]{
807 // Release buffer
808 gst_buffer_unmap(buffer, &info);
809 });
810
811 // Cache a few items
812 auto nChannels = c->mNumChannels;
813 sampleFormat fmt = c->mFmt;
814 samplePtr data = (samplePtr) info.data;
815 size_t samples = info.size / nChannels / SAMPLE_SIZE(fmt);
816
817 // Add sample data to tracks...depends on interleaved src data
818 for (int chn = 0; chn < nChannels; chn++)
819 {
820 // Append one channel
821 c->mChannels[chn]->Append(data,
822 fmt,
823 samples,
824 nChannels);
825
826 // Bump src to next channel
827 data += SAMPLE_SIZE(fmt);
828 }
829
830 return;
831}
#define str(a)
#define WARN(e, msg)
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
char * samplePtr
Definition: SampleFormat.h:55
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
sampleFormat mFmt

References floatSample, int16Sample, int24Sample, GStreamContext::mChannels, GStreamContext::mFmt, GStreamContext::mNumChannels, mPipeline, GStreamContext::mSampleRate, mTrackFactory, SAMPLE_SIZE, str, and WARN.

Referenced by GStreamerNewSample().

Here is the caller graph for this function:

◆ OnPadAdded()

void GStreamerImportFileHandle::OnPadAdded ( GstPad *  pad)

! Called when a pad-added signal comes in from UriDecodeBin

Parameters
pad- source pad of uridecodebin that will not be serving any data

Definition at line 535 of file ImportGStreamer.cpp.

536{
537 GStreamContext *c{};
538
539 {
540 // Retrieve the stream caps...skip stream if unavailable
541 GstCaps *caps = gst_pad_get_current_caps(pad);
542 GstCapsHandle handle{ caps };
543
544 if (!caps)
545 {
546 WARN(mPipeline.get(), ("OnPadAdded: unable to retrieve stream caps"));
547 return;
548 }
549
550 // Get the caps structure...no need to release
551 GstStructure *str = gst_caps_get_structure(caps, 0);
552 if (!str)
553 {
554 WARN(mPipeline.get(), ("OnPadAdded: unable to retrieve caps structure"));
555 return;
556 }
557
558 // Only accept audio streams...no need to release
559 const gchar *name = gst_structure_get_name(str);
560 if (!g_strrstr(name, "audio"))
561 {
562 WARN(mPipeline.get(), ("OnPadAdded: bypassing '%s' stream", name));
563 return;
564 }
565
566 {
567 // Allocate a NEW stream context
568 auto uc = std::make_unique<GStreamContext>();
569 c = uc.get();
570 if (!c)
571 {
572 WARN(mPipeline.get(), ("OnPadAdded: unable to allocate stream context"));
573 return;
574 }
575
576 // Set initial state
577 c->mUse = true;
578
579 // Always add it to the context list to keep the number of contexts
580 // in sync with the number of streams
582 // Pass the buck from uc
583 mStreams.push_back(std::move(uc));
584 }
585
586 c->mPipeline = mPipeline.get();
587
588 // Need pointer to context during pad removal (pad-remove signal)
589 SETCTX(pad, c);
590
591 // Save the stream's start time and duration
592 gst_pad_query_position(pad, GST_FORMAT_TIME, &c->mPosition);
593 gst_pad_query_duration(pad, GST_FORMAT_TIME, &c->mDuration);
594
595 // Retrieve the number of channels and validate
596 gint channels = -1;
597 gst_structure_get_int(str, "channels", &channels);
598 if (channels <= 0)
599 {
600 WARN(mPipeline.get(), ("OnPadAdded: channel count is invalid %d", channels));
601 return;
602 }
603 c->mNumChannels = channels;
604
605 // Retrieve the sample rate and validate
606 gint rate = -1;
607 gst_structure_get_int(str, "rate", &rate);
608 if (rate <= 0)
609 {
610 WARN(mPipeline.get(), ("OnPadAdded: sample rate is invalid %d", rate));
611 return;
612 }
613 c->mSampleRate = (double)rate;
614
615 c->mType.reset(g_strdup(name));
616 if (!c->mType)
617 {
618 WARN(mPipeline.get(), ("OnPadAdded: unable to allocate audio type"));
619 return;
620 }
621
622 // Done with capabilities
623 }
624
625 // Create audioconvert element
626 c->mConv = gst_element_factory_make("audioconvert", NULL);
627 if (!c->mConv)
628 {
629 WARN(mPipeline.get(), ("OnPadAdded: failed to create audioconvert element"));
630 return;
631 }
632
633 // Create appsink element
634 c->mSink = gst_element_factory_make("appsink", NULL);
635 if (!c->mSink)
636 {
637 WARN(mPipeline.get(), ("OnPadAdded: failed to create appsink element"));
638 return;
639 }
640 SETCTX(c->mSink, c);
641
642 // Set the appsink callbacks and add the context pointer
643 gst_app_sink_set_callbacks(GST_APP_SINK(c->mSink), &AppSinkCallbacks, this, NULL);
644
645 {
646 // Set the capabilities that we desire
647 GstCaps *caps = gst_static_caps_get(&supportedCaps);
648 GstCapsHandle handle{ caps };
649 if (!caps)
650 {
651 WARN(mPipeline.get(), ("OnPadAdded: failed to create static caps"));
652 return;
653 }
654 gst_app_sink_set_caps(GST_APP_SINK(c->mSink), caps);
655 }
656
657 // Do not sync to the clock...process as quickly as possible
658 gst_base_sink_set_sync(GST_BASE_SINK(c->mSink), FALSE);
659
660 // Don't drop buffers...allow queue to build unfettered
661 gst_app_sink_set_drop(GST_APP_SINK(c->mSink), FALSE);
662
663 // Add both elements to the pipeline
664 gst_bin_add_many(GST_BIN(mPipeline.get()), c->mConv, c->mSink, NULL);
665
666 // Link them together
667 if (!gst_element_link(c->mConv, c->mSink))
668 {
669 WARN(mPipeline.get(), ("OnPadAdded: failed to link audioconvert and appsink"));
670 return;
671 }
672
673 // Link the audiconvert sink pad to the src pad
674 GstPadLinkReturn ret = GST_PAD_LINK_OK;
675 {
676 GstObjHandle<GstPad> convsink{ gst_element_get_static_pad(c->mConv, "sink") };
677 if (convsink)
678 ret = gst_pad_link(pad, convsink.get());
679 if (!convsink || ret != GST_PAD_LINK_OK)
680 {
681 WARN(mPipeline.get(), ("OnPadAdded: failed to link uridecodebin to audioconvert - %d", ret));
682 return;
683 }
684 }
685
686 // Synchronize audioconvert state with parent
687 if (!gst_element_sync_state_with_parent(c->mConv))
688 {
689 WARN(mPipeline.get(), ("OnPadAdded: unable to sync audioconvert state"));
690 return;
691 }
692
693 // Synchronize appsink state with parent
694 if (!gst_element_sync_state_with_parent(c->mSink))
695 {
696 WARN(mPipeline.get(), ("OnPadAdded: unable to sync appaink state"));
697 return;
698 }
699
700 return;
701}
std::unique_ptr< GstCaps, Deleter< GstCaps, GstCapsUnref > > GstCapsHandle
#define SETCTX(o, c)
static GstAppSinkCallbacks AppSinkCallbacks
static GstStaticCaps supportedCaps

References AppSinkCallbacks, mPipeline, mStreams, mStreamsLock, name, SETCTX, str, supportedCaps, and WARN.

◆ OnPadRemoved()

void GStreamerImportFileHandle::OnPadRemoved ( GstPad *  pad)

! Called when a pad-removed signal comes in from UriDecodeBin

Parameters
pad- source pad of uridecodebin that will not be serving any data

Definition at line 706 of file ImportGStreamer.cpp.

707{
708 GStreamContext *c = GETCTX(pad);
709
710 // Set audioconvert and appsink states to NULL
711 gst_element_set_state(c->mSink, GST_STATE_NULL);
712 gst_element_set_state(c->mConv, GST_STATE_NULL);
713
714 // Unlink audioconvert -> appsink
715 gst_element_unlink(c->mConv, c->mSink);
716
717 // Remove the pads from the pipeilne
718 gst_bin_remove_many(GST_BIN(mPipeline.get()), c->mConv, c->mSink, NULL);
719
720 // And reset context
721 c->mConv = NULL;
722 c->mSink = NULL;
723
724 return;
725}
#define GETCTX(o)

References GETCTX, GStreamContext::mConv, mPipeline, and GStreamContext::mSink.

◆ OnTag()

void GStreamerImportFileHandle::OnTag ( GstAppSink *  appsink,
GstTagList *  tags 
)

! Called when a tag message comes in from the appsink

Parameters
appsink- Specific sink that received the message
tags- List of tags

Definition at line 1314 of file ImportGStreamer.cpp.

1315{
1316 // Collect all of the associates tags
1317 for (guint i = 0, icnt = gst_tag_list_n_tags(tags); i < icnt; i++)
1318 {
1319 wxString string;
1320
1321 // Get tag name...should always succeed...no need to release
1322 const gchar *name = gst_tag_list_nth_tag_name(tags, i);
1323 if (!name)
1324 {
1325 continue;
1326 }
1327
1328 // For each tag, determine its type and retrieve if possible
1329 for (guint j = 0, jcnt = gst_tag_list_get_tag_size(tags, name); j < jcnt; j++)
1330 {
1331 const GValue *val;
1332
1333 val = gst_tag_list_get_value_index(tags, name, j);
1334
1335 if (G_VALUE_HOLDS_STRING(val))
1336 {
1337 string = wxString::FromUTF8(g_value_get_string(val));
1338 }
1339 else if (G_VALUE_HOLDS_UINT(val))
1340 {
1341 string.Printf(wxT("%u"), (unsigned int) g_value_get_uint(val));
1342 }
1343 else if (G_VALUE_HOLDS_DOUBLE(val))
1344 {
1345 string.Printf(wxT("%g"), g_value_get_double(val));
1346 }
1347 else if (G_VALUE_HOLDS_BOOLEAN(val))
1348 {
1349 string = g_value_get_boolean(val) ? wxT("true") : wxT("false");
1350 }
1351 else if (GST_VALUE_HOLDS_DATE_TIME(val))
1352 {
1353 GstDateTime *dt = (GstDateTime *) g_value_get_boxed(val);
1354 GstString str{ gst_date_time_to_iso8601_string(dt) };
1355 string = wxString::FromUTF8(str.get());
1356 }
1357 else if (G_VALUE_HOLDS(val, G_TYPE_DATE))
1358 {
1359 GstString str{ gst_value_serialize(val) };
1360 string = wxString::FromUTF8(str.get());
1361 }
1362 else
1363 {
1364 wxLogMessage(wxT("Tag %s has unhandled type: %s"),
1365 wxString::FromUTF8(name),
1366 wxString::FromUTF8(G_VALUE_TYPE_NAME(val)));
1367 continue;
1368 }
1369
1370 // Translate known tag names
1371 wxString tag;
1372 if (strcmp(name, GST_TAG_TITLE) == 0)
1373 {
1374 tag = TAG_TITLE;
1375 }
1376 else if (strcmp(name, GST_TAG_ARTIST) == 0)
1377 {
1378 tag = TAG_ARTIST;
1379 }
1380 else if (strcmp(name, GST_TAG_ALBUM) == 0)
1381 {
1382 tag = TAG_ALBUM;
1383 }
1384 else if (strcmp(name, GST_TAG_TRACK_NUMBER) == 0)
1385 {
1386 tag = TAG_TRACK;
1387 }
1388 else if (strcmp(name, GST_TAG_DATE) == 0)
1389 {
1390 tag = TAG_YEAR;
1391 }
1392 else if (strcmp(name, GST_TAG_GENRE) == 0)
1393 {
1394 tag = TAG_GENRE;
1395 }
1396 else if (strcmp(name, GST_TAG_COMMENT) == 0)
1397 {
1398 tag = TAG_COMMENTS;
1399 }
1400 else
1401 {
1402 tag = wxString::FromUTF8(name);
1403 }
1404
1405 if (jcnt > 1)
1406 {
1407 tag.Printf(wxT("%s:%d"), tag, j);
1408 }
1409
1410 // Store the tag
1411 mTags.SetTag(tag, string);
1412 }
1413 }
1414}
std::unique_ptr< gchar, Deleter< void, g_free > > GstString
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:441

References mTags, name, Tags::SetTag(), str, TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_GENRE, TAG_TITLE, TAG_TRACK, TAG_YEAR, and wxT().

Referenced by ProcessBusMessage().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ProcessBusMessage()

bool GStreamerImportFileHandle::ProcessBusMessage ( bool &  success)

! Called when a message comes through GStreamer message bus

Parameters
success- will be set to true if successful
Returns
true if the loop should be terminated

Definition at line 1163 of file ImportGStreamer.cpp.

1164{
1165 bool cont = true;
1166
1167 // Default to no errors
1168 success = true;
1169
1170 // Get the next message
1171 std::unique_ptr < GstMessage, Deleter < GstMessage, GstMessageUnref > >
1172 msg{ gst_bus_timed_pop(mBus.get(), 100 * GST_MSECOND) };
1173
1174 if (!msg)
1175 {
1176 // Timed out...not an error
1177 return cont;
1178 }
1179
1180#if defined(_DEBUG)
1181 {
1182 GstString objname;
1183 if (msg->src != NULL)
1184 {
1185 objname.reset(gst_object_get_name(msg->src));
1186 }
1187
1188 wxLogMessage(wxT("GStreamer: Got %s%s%s"),
1189 wxString::FromUTF8(GST_MESSAGE_TYPE_NAME(msg.get())),
1190 objname ? wxT(" from ") : wxT(""),
1191 objname ? wxString::FromUTF8(objname.get()) : wxT(""));
1192 }
1193#endif
1194
1195 // Handle based on message type
1196 switch (GST_MESSAGE_TYPE(msg.get()))
1197 {
1198 // Handle error message from gstreamer
1199 case GST_MESSAGE_ERROR:
1200 {
1201 GErrorHandle err;
1202 GstString debug;
1203
1204 GstMessageParse(gst_message_parse_error, msg.get(), err, debug);
1205 if (err)
1206 {
1207 wxString m;
1208
1209 m.Printf(wxT("%s%s%s"),
1210 wxString::FromUTF8(err.get()->message),
1211 debug ? wxT("\n") : wxT(""),
1212 debug ? wxString::FromUTF8(debug.get()) : wxT(""));
1213 auto msg = XO("GStreamer Error: %s").Format( m );
1214#if defined(_DEBUG)
1215 AudacityMessageBox( msg );
1216#else
1217 wxLogMessage( msg.Debug() );
1218#endif
1219 }
1220
1221 success = false;
1222 cont = false;
1223 }
1224 break;
1225
1226 // Handle warning message from gstreamer
1227 case GST_MESSAGE_WARNING:
1228 {
1229 GErrorHandle err;
1230 GstString debug;
1231 GstMessageParse(gst_message_parse_warning, msg.get(), err, debug);
1232
1233 if (err)
1234 {
1235 wxLogMessage(wxT("GStreamer Warning: %s%s%s"),
1236 wxString::FromUTF8(err.get()->message),
1237 debug ? wxT("\n") : wxT(""),
1238 debug ? wxString::FromUTF8(debug.get()) : wxT(""));
1239 }
1240 }
1241 break;
1242
1243 // Handle warning message from gstreamer
1244 case GST_MESSAGE_INFO:
1245 {
1246 GErrorHandle err;
1247 GstString debug;
1248
1249 GstMessageParse(gst_message_parse_info, msg.get(), err, debug);
1250 if (err)
1251 {
1252 wxLogMessage(wxT("GStreamer Info: %s%s%s"),
1253 wxString::FromUTF8(err.get()->message),
1254 debug ? wxT("\n") : wxT(""),
1255 debug ? wxString::FromUTF8(debug.get()) : wxT(""));
1256 }
1257 }
1258 break;
1259
1260 // Handle metadata tags
1261 case GST_MESSAGE_TAG:
1262 {
1263 GstTagList *tags = NULL;
1264 auto cleanup = finally([&]{
1265 // Done with list
1266 if(tags) gst_tag_list_unref(tags);
1267 });
1268
1269 // Retrieve tag list from message...just ignore failure
1270 gst_message_parse_tag(msg.get(), &tags);
1271 if (tags)
1272 {
1273 // Go process the list
1274 OnTag(GST_APP_SINK(GST_MESSAGE_SRC(msg.get())), tags);
1275 }
1276 }
1277 break;
1278
1279 // Pre-roll is done...will happen for each group
1280 // (like with chained OGG files)
1281 case GST_MESSAGE_ASYNC_DONE:
1282 {
1283 // If this is the first async-done message, then tell
1284 // caller to end loop, but leave it active so that
1285 // gstreamer threads can still queue up.
1286 //
1287 // We'll receive multiple async-done messages for chained
1288 // ogg files, so ignore the message the 2nd and subsequent
1289 // occurrences.
1290 if (!mAsyncDone)
1291 {
1292 cont = false;
1293 mAsyncDone = true;
1294 }
1295
1296 }
1297 break;
1298
1299 // End of the stream (and all sub-streams)
1300 case GST_MESSAGE_EOS:
1301 {
1302 // Terminate loop
1303 cont = false;
1304 }
1305 break;
1306 }
1307
1308 return cont;
1309}
std::unique_ptr< GError, Deleter< GError, g_error_free > > GErrorHandle
void GstMessageParse(ParseFn fn, GstMessage *msg, GErrorHandle &err, GstString &debug)
void OnTag(GstAppSink *appsink, GstTagList *tags)

References AudacityMessageBox(), GstMessageParse(), mAsyncDone, mBus, OnTag(), wxT(), and XO().

Referenced by Import(), and Init().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetStreamUsage()

void GStreamerImportFileHandle::SetStreamUsage ( wxInt32  index,
bool  use 
)
overridevirtual

! Called by Import.cpp

Parameters
index- index of the stream in mStreamInfo and mStreams arrays
use- true if this stream should be imported, false otherwise

Implements ImportFileHandle.

Definition at line 905 of file ImportGStreamer.cpp.

906{
908 if ((guint) index < mStreams.size())
909 {
910 GStreamContext *c = mStreams[index].get();
911 c->mUse = use;
912 }
913}

References mStreams, mStreamsLock, and GStreamContext::mUse.

Member Data Documentation

◆ mAsyncDone

bool GStreamerImportFileHandle::mAsyncDone
private

true = 1st async-done message received

Definition at line 226 of file ImportGStreamer.cpp.

Referenced by GStreamerImportFileHandle(), and ProcessBusMessage().

◆ mBus

GstObjHandle<GstBus> GStreamerImportFileHandle::mBus
private

Message bus.

Definition at line 224 of file ImportGStreamer.cpp.

Referenced by Init(), and ProcessBusMessage().

◆ mDec

GstElement* GStreamerImportFileHandle::mDec
private

uridecodebin element

Definition at line 225 of file ImportGStreamer.cpp.

Referenced by GStreamerImportFileHandle(), Init(), and ~GStreamerImportFileHandle().

◆ mPipeline

GstObjHandle<GstElement> GStreamerImportFileHandle::mPipeline
private

GStreamer pipeline.

Definition at line 223 of file ImportGStreamer.cpp.

Referenced by Import(), Init(), OnNewSample(), OnPadAdded(), OnPadRemoved(), and ~GStreamerImportFileHandle().

◆ mStreamInfo

TranslatableStrings GStreamerImportFileHandle::mStreamInfo
private

Array of stream descriptions. Length is the same as mStreams.

Definition at line 218 of file ImportGStreamer.cpp.

Referenced by GetStreamCount(), GetStreamInfo(), and Init().

◆ mStreams

std::vector<std::unique_ptr<GStreamContext> > GStreamerImportFileHandle::mStreams
private

Array of pointers to stream contexts.

Definition at line 229 of file ImportGStreamer.cpp.

Referenced by Import(), Init(), OnPadAdded(), SetStreamUsage(), and ~GStreamerImportFileHandle().

◆ mStreamsLock

GMutex GStreamerImportFileHandle::mStreamsLock
private

Mutex protecting the mStreams array.

Definition at line 228 of file ImportGStreamer.cpp.

Referenced by GStreamerImportFileHandle(), Import(), Init(), OnPadAdded(), SetStreamUsage(), and ~GStreamerImportFileHandle().

◆ mTags

Tags GStreamerImportFileHandle::mTags
private

Tags to be passed back to Audacity.

Definition at line 219 of file ImportGStreamer.cpp.

Referenced by Import(), and OnTag().

◆ mTrackFactory

TrackFactory* GStreamerImportFileHandle::mTrackFactory
private

Factory to create tracks when samples arrive.

Definition at line 220 of file ImportGStreamer.cpp.

Referenced by GStreamerImportFileHandle(), Import(), and OnNewSample().

◆ mUri

GstString GStreamerImportFileHandle::mUri
private

URI of file.

Definition at line 222 of file ImportGStreamer.cpp.

Referenced by Init().


The documentation for this class was generated from the following file: