/* GStreamer * Copyright (C) <2017> Carlos Rafael Giani * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef __GST_NONSTREAM_AUDIO_DECODER_H__ #define __GST_NONSTREAM_AUDIO_DECODER_H__ #include #include #include G_BEGIN_DECLS typedef struct _GstNonstreamAudioDecoder GstNonstreamAudioDecoder; typedef struct _GstNonstreamAudioDecoderClass GstNonstreamAudioDecoderClass; /** * GstNonstreamAudioOutputMode: * @GST_NONSTREAM_AUDIO_OUTPUT_MODE_LOOPING: Playback position is moved back to the beginning of the loop * @GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY: Playback position increases steadily, even when looping * * The output mode defines how the output behaves with regards to looping. Either the playback position is * moved back to the beginning of the loop, acting like a backwards seek, or it increases steadily, as if * loop were "unrolled". */ typedef enum { GST_NONSTREAM_AUDIO_OUTPUT_MODE_LOOPING, GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY } GstNonstreamAudioOutputMode; /** * GstNonstreamAudioSubsongMode: * @GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE: Only the current subsong is played * @GST_NONSTREAM_AUDIO_SUBSONG_MODE_ALL: All subsongs are played (current subsong index is ignored) * @GST_NONSTREAM_AUDIO_SUBSONG_MODE_DECODER_DEFAULT: Use decoder specific default behavior * * The subsong mode defines how the decoder shall handle subsongs. */ typedef enum { GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE, GST_NONSTREAM_AUDIO_SUBSONG_MODE_ALL, GST_NONSTREAM_AUDIO_SUBSONG_MODE_DECODER_DEFAULT } GstNonstreamAudioSubsongMode; #define GST_TYPE_NONSTREAM_AUDIO_DECODER (gst_nonstream_audio_decoder_get_type()) #define GST_NONSTREAM_AUDIO_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_NONSTREAM_AUDIO_DECODER, GstNonstreamAudioDecoder)) #define GST_NONSTREAM_AUDIO_DECODER_CAST(obj) ((GstNonstreamAudioDecoder *)(obj)) #define GST_NONSTREAM_AUDIO_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_NONSTREAM_AUDIO_DECODER, GstNonstreamAudioDecoderClass)) #define GST_NONSTREAM_AUDIO_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_NONSTREAM_AUDIO_DECODER, GstNonstreamAudioDecoderClass)) #define GST_IS_NONSTREAM_AUDIO_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_NONSTREAM_AUDIO_DECODER)) #define GST_IS_NONSTREAM_AUDIO_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_NONSTREAM_AUDIO_DECODER)) /** * GST_NONSTREAM_AUDIO_DECODER_SINK_NAME: * * The name of the template for the sink pad. */ #define GST_NONSTREAM_AUDIO_DECODER_SINK_NAME "sink" /** * GST_NONSTREAM_AUDIO_DECODER_SRC_NAME: * * The name of the template for the source pad. */ #define GST_NONSTREAM_AUDIO_DECODER_SRC_NAME "src" /** * GST_NONSTREAM_AUDIO_DECODER_SINK_PAD: * @obj: base nonstream audio codec instance * * Gives the pointer to the sink #GstPad object of the element. */ #define GST_NONSTREAM_AUDIO_DECODER_SINK_PAD(obj) (((GstNonstreamAudioDecoder *) (obj))->sinkpad) /** * GST_NONSTREAM_AUDIO_DECODER_SRC_PAD: * @obj: base nonstream audio codec instance * * Gives the pointer to the source #GstPad object of the element. */ #define GST_NONSTREAM_AUDIO_DECODER_SRC_PAD(obj) (((GstNonstreamAudioDecoder *) (obj))->srcpad) /** * GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX: * @obj: base nonstream audio codec instance * * Locks the decoder mutex. * * Internally, the mutex is locked before one of the class vfuncs are * called, when position and duration queries are handled, and when * properties are set/retrieved. * * Derived classes should call lock during decoder related modifications * (for example, setting/clearing filter banks), when at the same time * audio might get decoded. An example are configuration changes that * happen when properties are set. Properties might be set from another * thread, so while the derived decoder is reconfigured, the mutex * should be locked. */ #define GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX(obj) g_mutex_lock(&(((GstNonstreamAudioDecoder *)(obj))->mutex)) #define GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX(obj) g_mutex_unlock(&(((GstNonstreamAudioDecoder *)(obj))->mutex)) /** * GstNonstreamAudioDecoder: * * The opaque #GstNonstreamAudioDecoder data structure. */ struct _GstNonstreamAudioDecoder { GstElement element; /*< protected > */ /* source and sink pads */ GstPad *sinkpad, *srcpad; /* loading information */ gint64 upstream_size; gboolean loaded_mode; GstAdapter *input_data_adapter; /* subsong states */ guint current_subsong; GstNonstreamAudioSubsongMode subsong_mode; GstClockTime subsong_duration; /* output states */ GstNonstreamAudioOutputMode output_mode; gint num_loops; gboolean output_format_changed; GstAudioInfo output_audio_info; /* The difference between these two values is: cur_pos_in_samples is * used for the GstBuffer offsets, while num_decoded_samples is used * for the segment base time values. * cur_pos_in_samples is reset after seeking, looping (when output mode * is LOOPING) and switching subsongs, while num_decoded is only reset * to 0 after a flushing seek (because flushing seeks alter the * pipeline's base_time). */ guint64 cur_pos_in_samples, num_decoded_samples; GstSegment cur_segment; gboolean discont; /* metadata */ GstToc *toc; /* allocation */ GstAllocator *allocator; GstAllocationParams allocation_params; /* thread safety */ GMutex mutex; }; /** * GstNonstreamAudioDecoderClass: * @element_class: The parent class structure * @seek: Optional. * Called when a seek event is received by the parent class. * new_position is a pointer to a GstClockTime integer which * contains a position relative to the current subsong. * Minimum is 0, maximum is the subsong length. * After this function finishes, new_position is set to the * actual new position (which may differ from the request * position, depending on the decoder). * @tell: Optional. * Called when a position query is received by the parent class. * The position that this function returns must be relative to * the current subsong. Thus, the minimum is 0, and the maximum * is the subsong length. * @load_from_buffer: Required if loads_from_sinkpad is set to TRUE (the default value). * Loads the media from the given buffer. The entire media is supplied at once, * so after this call, loading should be finished. This function * can also make use of a suggested initial subsong & subsong mode and initial * playback position (but isn't required to). In case it chooses a different starting * position, the function must pass this position to *initial_position. * The subclass does not have to unref the input buffer; the base class does that * already. * @load_from_custom: Required if loads_from_sinkpad is set to FALSE. * Loads the media in a way defined by the custom sink. Data is not supplied; * the derived class has to handle this on its own. Otherwise, this function is * identical to @load_from_buffer. * @get_main_tags: Optional. * Returns a tag list containing the main song tags, or NULL if there are * no such tags. Returned tags will be unref'd. Use this vfunc instead of * manually pushing a tag event downstream to avoid edge cases where not yet * pushed sticky tag events get overwritten before they are pushed (can for * example happen with decodebin if tags are pushed downstream before the * decodebin pads are linked). * @set_current_subsong: Optional. * Sets the current subsong. This function is allowed to switch to a different * subsong than the required one, and can optionally make use of the suggested initial * position. In case it chooses a different starting position, the function must pass * this position to *initial_position. * This function switches the subsong mode to GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE * automatically. * If this function is implemented by the subclass, @get_current_subsong and * @get_num_subsongs should be implemented as well. * @get_current_subsong: Optional. * Returns the current subsong. * If the current subsong mode is not GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE, this * function's return value is undefined. * If this function is implemented by the subclass, * @get_num_subsongs should be implemented as well. * @get_num_subsongs: Optional. * Returns the number of subsongs available. * The return values 0 and 1 have a similar, but distinct, meaning. * If this function returns 0, then this decoder does not support subsongs at all. * @get_current_subsong must then also always return 0. In other words, this function * either never returns 0, or never returns anything else than 0. * A return value of 1 means that the media contains either only one or no subsongs * (the entire song is then considered to be one single subsong). 1 also means that only * this very media has no or just one subsong, and the decoder itself can * support multiple subsongs. * @get_subsong_duration: Optional. * Returns the duration of a subsong. Returns GST_CLOCK_TIME_NONE if duration is unknown. * @get_subsong_tags: Optional. * Returns tags for a subsong, or NULL if there are no tags. * Returned tags will be unref'd. * @set_subsong_mode: Optional. * Sets the current subsong mode. Since this might influence the current playback position, * this function must set the initial_position integer argument to a defined value. * If the playback position is not affected at all, it must be set to GST_CLOCK_TIME_NONE. * If the subsong is restarted after the mode switch, it is recommended to set the value * to the position in the playback right after the switch (or 0 if the subsongs are always * reset back to the beginning). * @set_num_loops: Optional. * Sets the number of loops for playback. If this is called during playback, * the subclass must set any internal loop counters to zero. A loop value of -1 * means infinite looping; 0 means no looping; and when the num_loops is greater than 0, * playback should loop exactly num_loops times. If this function is implemented, * @get_num_loops should be implemented as well. The function can ignore the given values * and choose another; however, @get_num_loops should return this other value afterwards. * It is up to the subclass to define where the loop starts and ends. It can mean that only * a subset at the end or in the middle of a song is repeated, for example. * If the current subsong mode is GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE, then the subsong * is repeated this many times. If it is GST_NONSTREAM_AUDIO_SUBSONG_MODE_ALL, then all * subsongs are repeated this many times. With GST_NONSTREAM_AUDIO_SUBSONG_MODE_DECODER_DEFAULT, * the behavior is decoder specific. * @get_num_loops: Optional. * Returns the number of loops for playback. * @get_supported_output_modes: Always required. * Returns a bitmask containing the output modes the subclass supports. * The mask is formed by a bitwise OR combination of integers, which can be calculated * this way: 1 << GST_NONSTREAM_AUDIO_OUTPUT_MODE_ , where mode is either STEADY or LOOPING * @set_output_mode: Optional. * Sets the output mode the subclass has to use. Unlike with most other functions, the subclass * cannot choose a different mode; it must use the requested one. * If the output mode is set to LOOPING, @gst_nonstream_audio_decoder_handle_loop * must be called after playback moved back to the start of a loop. * @decode: Always required. * Allocates an output buffer, fills it with decoded audio samples, and must be passed on to * *buffer . The number of decoded samples must be passed on to *num_samples. * If decoding finishes or the decoding is no longer possible (for example, due to an * unrecoverable error), this function returns FALSE, otherwise TRUE. * @decide_allocation: Optional. * Sets up the allocation parameters for allocating output * buffers. The passed in query contains the result of the * downstream allocation query. * Subclasses should chain up to the parent implementation to * invoke the default handler. * @propose_allocation: Optional. * Proposes buffer allocation parameters for upstream elements. * Subclasses should chain up to the parent implementation to * invoke the default handler. * * Subclasses can override any of the available optional virtual methods or not, as * needed. At minimum, @load_from_buffer (or @load_from_custom), @get_supported_output_modes, * and @decode need to be overridden. * * All functions are called with a locked decoder mutex. * * If GST_ELEMENT_ERROR, GST_ELEMENT_WARNING, or GST_ELEMENT_INFO are called from * inside one of these functions, it is strongly recommended to unlock the decoder mutex * before and re-lock it after these macros to prevent potential deadlocks in case the * application does something with the element when it receives an ERROR/WARNING/INFO * message. Same goes for gst_element_post_message() calls and non-serialized events. * * By default, this class works by reading media data from the sinkpad, and then commencing * playback. Some decoders cannot be given data from a memory block, so the usual way of * reading all upstream data and passing it to @load_from_buffer doesn't work then. In this case, * set the value of loads_from_sinkpad to FALSE. This changes the way this class operates; * it does not require a sinkpad to exist anymore, and will call @load_from_custom instead. * One example of a decoder where this makes sense is UADE (Unix Amiga Delitracker Emulator). * For some formats (such as TFMX), it needs to do the file loading by itself. * Since most decoders can read input data from a memory block, the default value of * loads_from_sinkpad is TRUE. */ struct _GstNonstreamAudioDecoderClass { GstElementClass element_class; gboolean loads_from_sinkpad; /*< public > */ /* virtual methods for subclasses */ gboolean (*seek) (GstNonstreamAudioDecoder * dec, GstClockTime * new_position); GstClockTime (*tell) (GstNonstreamAudioDecoder * dec); gboolean (*load_from_buffer) (GstNonstreamAudioDecoder * dec, GstBuffer * source_data, guint initial_subsong, GstNonstreamAudioSubsongMode initial_subsong_mode, GstClockTime * initial_position, GstNonstreamAudioOutputMode * initial_output_mode, gint * initial_num_loops); gboolean (*load_from_custom) (GstNonstreamAudioDecoder * dec, guint initial_subsong, GstNonstreamAudioSubsongMode initial_subsong_mode, GstClockTime * initial_position, GstNonstreamAudioOutputMode * initial_output_mode, gint * initial_num_loops); GstTagList * (*get_main_tags) (GstNonstreamAudioDecoder * dec); gboolean (*set_current_subsong) (GstNonstreamAudioDecoder * dec, guint subsong, GstClockTime * initial_position); guint (*get_current_subsong) (GstNonstreamAudioDecoder * dec); guint (*get_num_subsongs) (GstNonstreamAudioDecoder * dec); GstClockTime (*get_subsong_duration) (GstNonstreamAudioDecoder * dec, guint subsong); GstTagList * (*get_subsong_tags) (GstNonstreamAudioDecoder * dec, guint subsong); gboolean (*set_subsong_mode) (GstNonstreamAudioDecoder * dec, GstNonstreamAudioSubsongMode mode, GstClockTime * initial_position); gboolean (*set_num_loops) (GstNonstreamAudioDecoder * dec, gint num_loops); gint (*get_num_loops) (GstNonstreamAudioDecoder * dec); guint (*get_supported_output_modes) (GstNonstreamAudioDecoder * dec); gboolean (*set_output_mode) (GstNonstreamAudioDecoder * dec, GstNonstreamAudioOutputMode mode, GstClockTime * current_position); gboolean (*decode) (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer, guint * num_samples); gboolean (*negotiate) (GstNonstreamAudioDecoder * dec); gboolean (*decide_allocation) (GstNonstreamAudioDecoder * dec, GstQuery * query); gboolean (*propose_allocation) (GstNonstreamAudioDecoder * dec, GstQuery * query); /*< private > */ gpointer _gst_reserved[GST_PADDING_LARGE]; }; GType gst_nonstream_audio_decoder_get_type (void); void gst_nonstream_audio_decoder_handle_loop (GstNonstreamAudioDecoder * dec, GstClockTime new_position); gboolean gst_nonstream_audio_decoder_set_output_format (GstNonstreamAudioDecoder * dec, GstAudioInfo const *audio_info); gboolean gst_nonstream_audio_decoder_set_output_format_simple (GstNonstreamAudioDecoder * dec, guint sample_rate, GstAudioFormat sample_format, guint num_channels); void gst_nonstream_audio_decoder_get_downstream_info (GstNonstreamAudioDecoder * dec, GstAudioFormat * format, gint * sample_rate, gint * num_channels); GstBuffer *gst_nonstream_audio_decoder_allocate_output_buffer (GstNonstreamAudioDecoder * dec, gsize size); G_END_DECLS #endif /* __GST_NONSTREAM_AUDIO_DECODER_H__ */