Audacity  3.0.3
ImportGStreamer.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 ImportGStreamer.cpp
6 
7 Copyright 2008 LRN
8 Based on ImportFFmpeg.cpp by LRN
9 
10 Rework for gstreamer 1.0 by LLL
11 
12 Licensed under the GNU General Public License v2 or later
13 
14 *//****************************************************************//****************************************************************//*******************************************************************/
25 
26 #include "../Audacity.h" // needed before GStreamer.h // for USE_* macros
27 
28 #if defined(USE_GSTREAMER)
29 #include "ImportGStreamer.h"
30 
31 #include <wx/window.h>
32 #include <wx/log.h>
33 
34 #define DESC XO("GStreamer-compatible files")
35 
36 
37 // On Windows we don't have configure script to turn this on or off,
38 // so let's use msw-specific pragma to add required libraries.
39 // Of course, library search path still has to be updated manually
40 #if defined(__WXMSW__)
41 # pragma comment(lib,"gstreamer-1.0.lib")
42 # pragma comment(lib,"gstapp-1.0.lib")
43 # pragma comment(lib,"gstbase-1.0.lib")
44 # pragma comment(lib,"glib-2.0.lib")
45 # pragma comment(lib,"gobject-2.0.lib")
46 #endif
47 
48 // all the includes live here by default
49 #include "../Tags.h"
50 #include "../WaveTrack.h"
51 
52 extern "C"
53 {
54 // #include <gio/gio.h>
55  #include <gst/app/gstappsink.h>
56  #include <gst/audio/audio-format.h>
57 }
58 
59 // Convenience macros
60 #define AUDCTX "audacity::context"
61 #define GETCTX(o) (GStreamContext *) g_object_get_data(G_OBJECT((o)), AUDCTX)
62 #define SETCTX(o, c) g_object_set_data(G_OBJECT((o)), AUDCTX, (gpointer) (c))
63 #define WARN(e, msg) GST_ELEMENT_WARNING((e), STREAM, FAILED, msg, (NULL));
64 
65 // Capabilities that Audacity can handle
66 //
67 // This resolves to: (on little endian)
68 //
69 // "audio/x-raw, "
70 // "format = (string) {S16LE, S24_32LE, F32LE}, "
71 // "rate = (int) [ 1, max ]",
72 // "channels = (int) [ 1, max ]"
73 
74 static GstStaticCaps supportedCaps =
75  GST_STATIC_CAPS(
76  GST_AUDIO_CAPS_MAKE(
77  "{"
78  GST_AUDIO_NE(S16) ", "
79  GST_AUDIO_NE(S24_32) ", "
80  GST_AUDIO_NE(F32)
81  "}"
82  )
83  );
84 
86 {
87  explicit g_mutex_locker(GMutex &mutex_)
88  : mutex(mutex_)
89  {
90  g_mutex_lock(&mutex);
91  }
92 
94  {
95  g_mutex_unlock(&mutex);
96  }
97 
98  GMutex &mutex;
99 };
100 
101 template<typename T, void(*Fn)(T*)> struct Deleter {
102  inline void operator() (void *p) const
103  {
104  if (p)
105  Fn(static_cast<T*>(p));
106  }
107 };
108 using GstString = std::unique_ptr < gchar, Deleter<void, g_free> > ;
109 using GErrorHandle = std::unique_ptr < GError, Deleter<GError, g_error_free> > ;
110 
111 using ParseFn = void (*)(GstMessage *message, GError **gerror, gchar **debug);
112 inline void GstMessageParse(ParseFn fn, GstMessage *msg, GErrorHandle &err, GstString &debug)
113 {
114  GError *error;
115  gchar *string;
116  fn(msg, &error, &string);
117  err.reset(error);
118  debug.reset(string);
119 }
120 
121 // Context used for private stream data
123 {
124  GstElement *mConv{}; // Audio converter
125  GstElement *mSink{}; // Application sink
126  bool mUse{}; // True if this stream should be imported
127  NewChannelGroup mChannels; // Array of WaveTrack pointers, one for each channel
128  unsigned mNumChannels{}; // Number of channels
129  gdouble mSampleRate{}; // Sample rate
130  GstString mType; // Audio type
131  sampleFormat mFmt{ floatSample }; // Sample format
132  gint64 mPosition{}; // Start position of stream
133  gint64 mDuration{}; // Duration of stream
134  GstElement *mPipeline{};
135 
138  {
139  // Remove the appsink element
140  if (mSink)
141  {
142  gst_bin_remove(GST_BIN(mPipeline), mSink);
143  }
144 
145  // Remove the audioconvert element
146  if (mConv)
147  {
148  gst_bin_remove(GST_BIN(mPipeline), mConv);
149  }
150  }
151 };
152 
153 // For RAII on gst objects
154 template<typename T> using GstObjHandle =
155  std::unique_ptr < T, Deleter<void, gst_object_unref > > ;
156 
159 {
160 public:
161  GStreamerImportFileHandle(const wxString & name);
162  virtual ~GStreamerImportFileHandle();
163 
166  bool Init();
167 
170 
173  wxInt32 GetStreamCount() override;
174 
177  const TranslatableStrings &GetStreamInfo() override;
178 
182  void SetStreamUsage(wxInt32 index, bool use) override;
183 
186  int Import(TrackFactory *trackFactory,
187  TrackHolders &outTracks,
188  Tags *tags) override;
189 
190  // =========================================================================
191  // Handled within the gstreamer threads
192  // =========================================================================
193 
196  void OnPadAdded(GstPad *pad);
197 
200  void OnPadRemoved(GstPad *pad);
201 
205  bool ProcessBusMessage(bool & success);
206 
210  void OnTag(GstAppSink *appsink, GstTagList *tags);
211 
215  void OnNewSample(GStreamContext *c, GstSample *sample);
216 
217 private:
220  TrackFactory *mTrackFactory;
221 
225  GstElement *mDec;
226  bool mAsyncDone;
227 
228  GMutex mStreamsLock;
229  std::vector<std::unique_ptr<GStreamContext>> mStreams;
230 };
231 
235 {
236 public:
239 
241  virtual ~GStreamerImportPlugin();
242 
244 
245  wxString GetPluginStringID() override;
246 
248 
250  std::unique_ptr<ImportFileHandle> Open(
251  const wxString &Filename, AudacityProject*) override;
252 };
253 
254 // ============================================================================
255 // Initialization
256 // ============================================================================
257 
258 // ----------------------------------------------------------------------------
259 // Instantiate GStreamerImportPlugin and add to the list of known importers
260 
261 static
263  []() -> std::unique_ptr< ImportPlugin > {
264  wxLogMessage(_TS("Audacity is built against GStreamer version %d.%d.%d-%d"),
265  GST_VERSION_MAJOR,
266  GST_VERSION_MINOR,
267  GST_VERSION_MICRO,
268  GST_VERSION_NANO);
269 
270  // Initialize gstreamer
271  GErrorHandle error;
272  bool initError;
273  {
274  int argc = 0;
275  char **argv = NULL;
276  GError *ee;
277  initError = !gst_init_check(&argc, &argv, &ee);
278  error.reset(ee);
279  }
280  if ( initError )
281  {
282  wxLogMessage(wxT("Failed to initialize GStreamer. Error %d: %s"),
283  error.get()->code,
284  wxString::FromUTF8(error.get()->message));
285  return {};
286  }
287 
288  guint major, minor, micro, nano;
289  gst_version(&major, &minor, &micro, &nano);
290  wxLogMessage(wxT("Linked to GStreamer version %d.%d.%d-%d"),
291  major,
292  minor,
293  micro,
294  nano);
295 
296  // Instantiate plugin
297  auto plug = std::make_unique<GStreamerImportPlugin>();
298 
299  // No supported extensions...no gstreamer plugins installed
300  if (plug->GetSupportedExtensions().size() == 0)
301  return {};
302 
303  // Add to list of importers
304  return std::move(plug);
305 }() } registered;
306 
307 // ============================================================================
308 // GStreamerImportPlugin Class
309 // ============================================================================
310 
311 // ----------------------------------------------------------------------------
312 // Constructor
314 : ImportPlugin( {} )
315 {
316 }
317 
318 // ----------------------------------------------------------------------------
319 // Destructor
321 {
322 }
323 
324 // ----------------------------------------------------------------------------
325 // Return the plugin description
328 {
329  return DESC;
330 }
331 
332 // ----------------------------------------------------------------------------
333 // Return the plugin name
334 wxString
336 {
337  return wxT("gstreamer");
338 }
339 
340 // Obtains a list of supported extensions from typefind factories
341 // TODO: improve the list. It is obviously incomplete.
344 {
345  // We refresh the extensions each time this is called in case the
346  // user had installed additional gstreamer plugins while Audacity
347  // was active.
348  mExtensions.clear();
349 
350  // Gather extensions from all factories that support audio
351  {
352  std::unique_ptr < GList, Deleter<GList, gst_plugin_feature_list_free> >
353  factories{ gst_type_find_factory_get_list() };
354 
355  for (GList *list = factories.get(); list != NULL; list = g_list_next(list))
356  {
357  GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY(list->data);
358 
359  // We need the capabilities to determine if it handles audio
360  GstCaps *caps = gst_type_find_factory_get_caps(factory);
361  if (!caps)
362  {
363  continue;
364  }
365 
366  // Check each structure in the caps for audio
367  for (guint c = 0, clen = gst_caps_get_size(caps); c < clen; c++)
368  {
369  // Bypass if it isn't for audio
370  GstStructure *str = gst_caps_get_structure(caps, c);
371  if (!g_str_has_prefix(gst_structure_get_name(str), "audio"))
372  {
373  continue;
374  }
375 
376  // This factory can handle audio, so get the extensions
377  const gchar *const *extensions = gst_type_find_factory_get_extensions(factory);
378  if (!extensions)
379  {
380  continue;
381  }
382 
383  // Add each extension to the list
384  for (guint i = 0; extensions[i] != NULL; i++)
385  {
386  wxString extension = wxString::FromUTF8(extensions[i]);
387  if (mExtensions.Index(extension, false) == wxNOT_FOUND)
388  {
389  mExtensions.push_back(extension);
390  }
391  }
392  }
393  }
394  }
395 
396  // Get them in a decent order
397  mExtensions.Sort();
398 
399  // Log it for debugging
400  wxString extensions = wxT("Extensions:");
401  for (size_t i = 0; i < mExtensions.size(); i++)
402  {
403  extensions = extensions + wxT(" ") + mExtensions[i];
404  }
405  wxLogMessage(wxT("%s"), extensions);
406 
407  return mExtensions;
408 }
409 
410 // ----------------------------------------------------------------------------
411 // Open the file and return an importer "file handle"
412 std::unique_ptr<ImportFileHandle> GStreamerImportPlugin::Open(
413  const wxString &filename, AudacityProject*)
414 {
415  auto handle = std::make_unique<GStreamerImportFileHandle>(filename);
416 
417  // Initialize the handle
418  if (!handle->Init())
419  {
420  return nullptr;
421  }
422 
423  // This std::move is needed to "upcast" the pointer type
424  return std::move(handle);
425 }
426 
427 // ============================================================================
428 // GStreamerImportFileHandle Class
429 // ============================================================================
430 
431 // ----------------------------------------------------------------------------
432 // The following methods/functions run within gstreamer thread contexts. No
433 // interaction with wxWidgets should be allowed. See explanation at the top
434 // of this file.
435 // ----------------------------------------------------------------------------
436 
437 // ----------------------------------------------------------------------------
438 // Filter out any video streams
439 //
440 // LRN found that by doing this here, video streams aren't decoded thus
441 // reducing the time/processing needed to extract the audio streams.
442 //
443 // This "gint" is really GstAutoplugSelectResult enum
444 static gint
445 GStreamerAutoplugSelectCallback(GstElement * WXUNUSED(element),
446  GstPad * WXUNUSED(pad),
447  GstCaps * WXUNUSED(caps),
448  GstElementFactory *factory,
449  gpointer WXUNUSED(data))
450 {
451  // Check factory class
452  const gchar *fclass = gst_element_factory_get_klass(factory);
453 
454  // Skip video decoding
455  if (g_strrstr(fclass,"Video"))
456  {
457  return 2; // GST_AUTOPLUG_SELECT_SKIP
458  }
459 
460  return 0; // GST_AUTOPLUG_SELECT_TRY
461 }
462 
463 // ----------------------------------------------------------------------------
464 // Handle the "pad-added" signal from uridecodebin
465 static void
466 GStreamerPadAddedCallback(GstElement * WXUNUSED(element),
467  GstPad *pad,
468  gpointer data)
469 {
470  ((GStreamerImportFileHandle *) data)->OnPadAdded(pad);
471 
472  return;
473 }
474 
475 // ----------------------------------------------------------------------------
476 // Handle the "pad-removed" signal from uridecodebin
477 static void
478 GStreamerPadRemovedCallback(GstElement * WXUNUSED(element),
479  GstPad *pad,
480  gpointer data)
481 {
482  ((GStreamerImportFileHandle *) data)->OnPadRemoved(pad);
483 
484  return;
485 }
486 
487 // ----------------------------------------------------------------------------
488 // Handle the "NEW-sample" signal from uridecodebin
489 inline void GstSampleUnref(GstSample *p) { gst_sample_unref(p); } // I can't use the static function name directly...
490 
491 static GstFlowReturn
492 GStreamerNewSample(GstAppSink *appsink, gpointer data)
493 {
494  // Don't let C++ exceptions propagate through GStreamer
495  return GuardedCall< GstFlowReturn > ( [&] {
497  static GMutex mutex;
498 
499  // Get the sample
500  std::unique_ptr < GstSample, Deleter< GstSample, GstSampleUnref> >
501  sample{ gst_app_sink_pull_sample(appsink) };
502 
503  // We must single thread here to prevent concurrent use of the
504  // Audacity track functions.
505  g_mutex_locker locker{ mutex };
506 
507  handle->OnNewSample(GETCTX(appsink), sample.get());
508 
509  return GST_FLOW_OK;
510  }, MakeSimpleGuard(GST_FLOW_ERROR) );
511 }
512 
513 // ----------------------------------------------------------------------------
514 // AppSink callbacks are used instead of signals to reduce processing
515 static GstAppSinkCallbacks AppSinkCallbacks =
516 {
517  NULL, // eos
518  NULL, // new_preroll
519  GStreamerNewSample // new_sample
520 };
521 
522 static GstAppSinkCallbacks AppSinkBitBucket =
523 {
524  NULL, // eos
525  NULL, // new_preroll
526  NULL // new_sample
527 };
528 
529 inline void GstCapsUnref(GstCaps *p) { gst_caps_unref(p); } // I can't use the static function name directly...
530 using GstCapsHandle = std::unique_ptr < GstCaps, Deleter<GstCaps, GstCapsUnref> >;
531 
532 // ----------------------------------------------------------------------------
533 // Handle the "pad-added" message
534 void
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 }
702 
703 // ----------------------------------------------------------------------------
704 // Process the "pad-removed" signal from uridecodebin
705 void
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 }
726 
727 // ----------------------------------------------------------------------------
728 // Handle the "NEW-sample" message
729 void
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 }
832 
833 // ----------------------------------------------------------------------------
834 // The following methods run within the main thread.
835 // ----------------------------------------------------------------------------
836 
837 // ----------------------------------------------------------------------------
838 // Constructor
841 {
842  mDec = NULL;
843  mTrackFactory = NULL;
844  mAsyncDone = false;
845 
846  g_mutex_init(&mStreamsLock);
847 }
848 
849 // ----------------------------------------------------------------------------
850 // Destructor
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  {
866  g_mutex_locker locker{ mStreamsLock };
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 }
885 
886 // ----------------------------------------------------------------------------
887 // Return number of readable audio streams in the file
888 wxInt32
890 {
891  return mStreamInfo.size();
892 }
893 
894 // ----------------------------------------------------------------------------
895 // Return array of strings - descriptions of the streams
896 const TranslatableStrings &
898 {
899  return mStreamInfo;
900 }
901 
902 // ----------------------------------------------------------------------------
903 // Mark streams to process as selected by the user
904 void
906 {
907  g_mutex_locker locker{ mStreamsLock };
908  if ((guint) index < mStreams.size())
909  {
910  GStreamContext *c = mStreams[index].get();
911  c->mUse = use;
912  }
913 }
914 
915 // ----------------------------------------------------------------------------
916 // Initialize importer
917 bool
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
980  g_mutex_locker locker{ mStreamsLock };
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 }
997 
998 // ----------------------------------------------------------------------------
999 // Return file dialog filter description
1002 {
1003  return DESC;
1004 }
1005 
1006 // ----------------------------------------------------------------------------
1007 // Return number of uncompressed bytes in file...doubtful this is possible
1008 auto
1010 {
1011  return 0;
1012 }
1013 
1014 // ----------------------------------------------------------------------------
1015 // Import streams
1016 int
1017 GStreamerImportFileHandle::Import(TrackFactory *trackFactory,
1018  TrackHolders &outTracks,
1019  Tags *tags)
1020 {
1021  outTracks.clear();
1022 
1023  // Save track factory pointer
1024  mTrackFactory = trackFactory;
1025 
1026  // Create the progress dialog
1027  CreateProgress();
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 }
1153 
1154 // ----------------------------------------------------------------------------
1155 // Message handlers
1156 // ----------------------------------------------------------------------------
1157 
1158 inline void GstMessageUnref(GstMessage *p) { gst_message_unref(p); }
1159 
1160 // ----------------------------------------------------------------------------
1161 // Retrieve and process a bus message
1162 bool
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 }
1310 
1311 // ----------------------------------------------------------------------------
1312 // Handle the "tag" message
1313 void
1314 GStreamerImportFileHandle::OnTag(GstAppSink * WXUNUSED(appsink), GstTagList *tags)
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 }
1415 #endif //USE_GSTREAMER
supportedCaps
static GstStaticCaps supportedCaps
Definition: ImportGStreamer.cpp:74
GStreamerImportPlugin::Open
std::unique_ptr< ImportFileHandle > Open(const wxString &Filename, AudacityProject *) override
! Probes the file and opens it if appropriate
Definition: ImportGStreamer.cpp:412
GStreamContext::mNumChannels
unsigned mNumChannels
Definition: ImportGStreamer.cpp:128
GStreamerImportFileHandle::Init
bool Init()
Definition: ImportGStreamer.cpp:918
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
BasicUI::ProgressResult::Success
@ Success
GstCapsHandle
std::unique_ptr< GstCaps, Deleter< GstCaps, GstCapsUnref > > GstCapsHandle
Definition: ImportGStreamer.cpp:530
GStreamerImportPlugin
An ImportPlugin for GStreamer data.
Definition: ImportGStreamer.cpp:235
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
fn
static const auto fn
Definition: WaveformView.cpp:1108
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
ImportPlugin
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
str
#define str(a)
Definition: DBConnection.cpp:30
GStreamerImportFileHandle::Import
int Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportGStreamer.cpp:1017
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
GStreamerImportFileHandle::mUri
GstString mUri
URI of file.
Definition: ImportGStreamer.cpp:222
TAG_TRACK
#define TAG_TRACK
Definition: Tags.h:63
GStreamerImportFileHandle::mAsyncDone
bool mAsyncDone
true = 1st async-done message received
Definition: ImportGStreamer.cpp:226
GStreamerImportFileHandle::mBus
GstObjHandle< GstBus > mBus
Message bus.
Definition: ImportGStreamer.cpp:224
Deleter
Definition: ImportGStreamer.cpp:101
GStreamerImportFileHandle::~GStreamerImportFileHandle
virtual ~GStreamerImportFileHandle()
Definition: ImportGStreamer.cpp:851
MakeSimpleGuard
SimpleGuard< R > MakeSimpleGuard(R value)
Convert a value to a handler function returning that value, suitable for GuardedCall<R>
Definition: AudacityException.h:167
g_mutex_locker::g_mutex_locker
g_mutex_locker(GMutex &mutex_)
Definition: ImportGStreamer.cpp:87
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
GStreamContext::mType
GstString mType
Definition: ImportGStreamer.cpp:130
XO
#define XO(s)
Definition: Internat.h:31
GStreamContext
Definition: ImportGStreamer.cpp:123
AppSinkBitBucket
static GstAppSinkCallbacks AppSinkBitBucket
Definition: ImportGStreamer.cpp:522
int24Sample
@ int24Sample
Definition: SampleFormat.h:33
GStreamerPadRemovedCallback
static void GStreamerPadRemovedCallback(GstElement *WXUNUSED(element), GstPad *pad, gpointer data)
Definition: ImportGStreamer.cpp:478
GStreamContext::mChannels
NewChannelGroup mChannels
Definition: ImportGStreamer.cpp:127
registered
static Importer::RegisteredImportPlugin registered
Definition: ImportAUP.cpp:261
GstObjHandle
std::unique_ptr< T, Deleter< void, gst_object_unref > > GstObjHandle
Definition: ImportGStreamer.cpp:155
ImportFileHandle::mProgress
std::unique_ptr< ProgressDialog > mProgress
Definition: ImportPlugin.h:159
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
ImportPlugin::mExtensions
const FileExtensions mExtensions
Definition: ImportPlugin.h:99
GstString
std::unique_ptr< gchar, Deleter< void, g_free > > GstString
Definition: ImportGStreamer.cpp:108
GStreamerImportPlugin::~GStreamerImportPlugin
virtual ~GStreamerImportPlugin()
! Destructor
Definition: ImportGStreamer.cpp:320
g_mutex_locker::mutex
GMutex & mutex
Definition: ImportGStreamer.cpp:98
_TS
#define _TS(s)
Definition: Internat.h:27
floatSample
@ floatSample
Definition: SampleFormat.h:34
GErrorHandle
std::unique_ptr< GError, Deleter< GError, g_error_free > > GErrorHandle
Definition: ImportGStreamer.cpp:109
ImportFileHandle::CreateProgress
void CreateProgress()
Definition: ImportPlugin.cpp:45
GStreamerImportFileHandle::ProcessBusMessage
bool ProcessBusMessage(bool &success)
Definition: ImportGStreamer.cpp:1163
GETCTX
#define GETCTX(o)
Definition: ImportGStreamer.cpp:61
GStreamerNewSample
static GstFlowReturn GStreamerNewSample(GstAppSink *appsink, gpointer data)
Definition: ImportGStreamer.cpp:492
Tags::SetTag
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:486
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:817
GStreamerAutoplugSelectCallback
static gint GStreamerAutoplugSelectCallback(GstElement *WXUNUSED(element), GstPad *WXUNUSED(pad), GstCaps *WXUNUSED(caps), GstElementFactory *factory, gpointer WXUNUSED(data))
Definition: ImportGStreamer.cpp:445
AppSinkCallbacks
static GstAppSinkCallbacks AppSinkCallbacks
Definition: ImportGStreamer.cpp:515
GStreamerImportFileHandle::SetStreamUsage
void SetStreamUsage(wxInt32 index, bool use) override
Definition: ImportGStreamer.cpp:905
int16Sample
@ int16Sample
Definition: SampleFormat.h:32
GStreamerImportPlugin::GetSupportedExtensions
FileExtensions GetSupportedExtensions() override
Definition: ImportGStreamer.cpp:343
GStreamerPadAddedCallback
static void GStreamerPadAddedCallback(GstElement *WXUNUSED(element), GstPad *pad, gpointer data)
Definition: ImportGStreamer.cpp:466
name
const TranslatableString name
Definition: Distortion.cpp:98
GstSampleUnref
void GstSampleUnref(GstSample *p)
Definition: ImportGStreamer.cpp:489
GStreamerImportPlugin::GStreamerImportPlugin
GStreamerImportPlugin()
! Constructor
Definition: ImportGStreamer.cpp:313
GStreamerImportFileHandle::GStreamerImportFileHandle
GStreamerImportFileHandle(const wxString &name)
Definition: ImportGStreamer.cpp:839
GStreamContext::mConv
GstElement * mConv
Definition: ImportGStreamer.cpp:124
GStreamerImportFileHandle::GetStreamInfo
const TranslatableStrings & GetStreamInfo() override
Definition: ImportGStreamer.cpp:897
GstCapsUnref
void GstCapsUnref(GstCaps *p)
Definition: ImportGStreamer.cpp:529
TAG_GENRE
#define TAG_GENRE
Definition: Tags.h:65
GStreamContext::mDuration
gint64 mDuration
Definition: ImportGStreamer.cpp:133
GStreamerImportPlugin::GetPluginFormatDescription
TranslatableString GetPluginFormatDescription() override
Definition: ImportGStreamer.cpp:327
ImportFileHandle::mFilename
FilePath mFilename
Definition: ImportPlugin.h:158
GStreamerImportFileHandle::OnNewSample
void OnNewSample(GStreamContext *c, GstSample *sample)
Definition: ImportGStreamer.cpp:730
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
GstMessageParse
void GstMessageParse(ParseFn fn, GstMessage *msg, GErrorHandle &err, GstString &debug)
Definition: ImportGStreamer.cpp:112
ImportFileHandle
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
GStreamerImportFileHandle::OnPadRemoved
void OnPadRemoved(GstPad *pad)
Definition: ImportGStreamer.cpp:706
GStreamerImportFileHandle::mPipeline
GstObjHandle< GstElement > mPipeline
GStreamer pipeline.
Definition: ImportGStreamer.cpp:223
GStreamContext::GStreamContext
GStreamContext()
Definition: ImportGStreamer.cpp:136
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
GStreamerImportFileHandle::GetFileDescription
TranslatableString GetFileDescription() override
Definition: ImportGStreamer.cpp:1001
GStreamerImportFileHandle::mDec
GstElement * mDec
uridecodebin element
Definition: ImportGStreamer.cpp:225
GStreamerImportFileHandle::mStreams
std::vector< std::unique_ptr< GStreamContext > > mStreams
Array of pointers to stream contexts.
Definition: ImportGStreamer.cpp:229
GStreamerImportFileHandle::mTags
Tags mTags
Tags to be passed back to Audacity.
Definition: ImportGStreamer.cpp:219
GstMessageUnref
void GstMessageUnref(GstMessage *p)
Definition: ImportGStreamer.cpp:1158
GStreamContext::mPipeline
GstElement * mPipeline
Definition: ImportGStreamer.cpp:134
GStreamContext::~GStreamContext
~GStreamContext()
Definition: ImportGStreamer.cpp:137
GStreamContext::mSink
GstElement * mSink
Definition: ImportGStreamer.cpp:125
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
GStreamerImportPlugin::GetPluginStringID
wxString GetPluginStringID() override
Definition: ImportGStreamer.cpp:335
g_mutex_locker
Definition: ImportGStreamer.cpp:86
DESC
#define DESC
Definition: ImportGStreamer.cpp:34
GStreamerImportFileHandle::GetFileUncompressedBytes
ByteCount GetFileUncompressedBytes() override
Definition: ImportGStreamer.cpp:1009
Importer::RegisteredImportPlugin
Definition: Import.h:85
ParseFn
void(*)(GstMessage *message, GError **gerror, gchar **debug) ParseFn
Definition: ImportGStreamer.cpp:111
TAG_COMMENTS
#define TAG_COMMENTS
Definition: Tags.h:66
TAG_ARTIST
#define TAG_ARTIST
Definition: Tags.h:61
GStreamerImportFileHandle::mStreamsLock
GMutex mStreamsLock
Mutex protecting the mStreams array.
Definition: ImportGStreamer.cpp:228
GStreamerImportFileHandle::mTrackFactory
TrackFactory * mTrackFactory
Factory to create tracks when samples arrive.
Definition: ImportGStreamer.cpp:220
GStreamContext::mUse
bool mUse
Definition: ImportGStreamer.cpp:126
SETCTX
#define SETCTX(o, c)
Definition: ImportGStreamer.cpp:62
GStreamContext::mPosition
gint64 mPosition
Definition: ImportGStreamer.cpp:132
GStreamContext::mFmt
sampleFormat mFmt
Definition: ImportGStreamer.cpp:131
NewChannelGroup
std::vector< std::shared_ptr< WaveTrack > > NewChannelGroup
Definition: Import.cpp:62
ImportFileHandle::ByteCount
unsigned long long ByteCount
Definition: ImportPlugin.h:127
WARN
#define WARN(e, msg)
Definition: ImportGStreamer.cpp:63
GStreamerImportFileHandle::OnPadAdded
void OnPadAdded(GstPad *pad)
Definition: ImportGStreamer.cpp:535
GStreamerImportFileHandle::GetStreamCount
wxInt32 GetStreamCount() override
Definition: ImportGStreamer.cpp:889
GStreamerImportFileHandle::mStreamInfo
TranslatableStrings mStreamInfo
Array of stream descriptions. Length is the same as mStreams.
Definition: ImportGStreamer.cpp:218
GStreamerImportFileHandle
! Does actual import, returned by GStreamerImportPlugin::Open
Definition: ImportGStreamer.cpp:159
Deleter::operator()
void operator()(void *p) const
Definition: ImportGStreamer.cpp:102
TAG_TITLE
#define TAG_TITLE
Definition: Tags.h:60
g_mutex_locker::~g_mutex_locker
~g_mutex_locker()
Definition: ImportGStreamer.cpp:93
GStreamerImportFileHandle::OnTag
void OnTag(GstAppSink *appsink, GstTagList *tags)
Definition: ImportGStreamer.cpp:1314
GStreamContext::mSampleRate
gdouble mSampleRate
Definition: ImportGStreamer.cpp:129
TAG_ALBUM
#define TAG_ALBUM
Definition: Tags.h:62