static void gst_bus_dispose (GObject * object) { GstBus *bus = GST_BUS (object); if (bus->priv->queue) { GstMessage *message; g_mutex_lock (&bus->priv->queue_lock); do { message = gst_atomic_queue_pop (bus->priv->queue); if (message) gst_message_unref (message); } while (message != NULL); gst_atomic_queue_unref (bus->priv->queue); bus->priv->queue = NULL; g_mutex_unlock (&bus->priv->queue_lock); g_mutex_clear (&bus->priv->queue_lock); if (bus->priv->poll) gst_poll_free (bus->priv->poll); bus->priv->poll = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); }
static void clear_packet_queue(GstAtomicQueue *queue) { GstDataQueueItem *item; while ((item = gst_atomic_queue_pop(queue))) { item->destroy(item); } }
static GstFlowReturn default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstFlowReturn result; GstBufferPoolPrivate *priv = pool->priv; while (TRUE) { if (G_UNLIKELY (GST_BUFFER_POOL_IS_FLUSHING (pool))) goto flushing; /* try to get a buffer from the queue */ *buffer = gst_atomic_queue_pop (priv->queue); if (G_LIKELY (*buffer)) { gst_poll_read_control (priv->poll); result = GST_FLOW_OK; GST_LOG_OBJECT (pool, "acquired buffer %p", *buffer); break; } /* no buffer, try to allocate some more */ GST_LOG_OBJECT (pool, "no buffer, trying to allocate"); result = do_alloc_buffer (pool, buffer, NULL); if (G_LIKELY (result == GST_FLOW_OK)) /* we have a buffer, return it */ break; if (G_UNLIKELY (result != GST_FLOW_EOS)) /* something went wrong, return error */ break; /* check if we need to wait */ if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT)) { GST_LOG_OBJECT (pool, "no more buffers"); break; } /* now we release the control socket, we wait for a buffer release or * flushing */ gst_poll_read_control (pool->priv->poll); GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); gst_poll_write_control (pool->priv->poll); } return result; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pool, "we are flushing"); return GST_FLOW_FLUSHING; } }
/* must be called with the lock */ static gboolean default_stop (GstBufferPool * pool) { GstBufferPoolPrivate *priv = pool->priv; GstBuffer *buffer; /* clear the pool */ while ((buffer = gst_atomic_queue_pop (priv->queue))) { gst_poll_read_control (priv->poll); do_free_buffer (pool, buffer); } return priv->cur_buffers == 0; }
static void approve_transmit_cb(guint stream_id, GstScreamQueue *self) { GstScreamDataQueueRtpItem *item; GstScreamStream *stream; g_rw_lock_reader_lock(&self->lock); stream = g_hash_table_lookup(self->streams, GUINT_TO_POINTER(stream_id)); g_rw_lock_reader_unlock(&self->lock); if ((item = gst_atomic_queue_pop(stream->packet_queue))) { stream->enqueued_payload_size -= item->rtp_payload_size; stream->enqueued_packets--; GST_LOG_OBJECT(self, "approving: pt = %u, seq: %u, pass: %u", item->rtp_pt, item->rtp_seq, self->pass_through); gst_data_queue_push(self->approved_packets, (GstDataQueueItem *)item); } else GST_LOG_OBJECT(self, "Got approve callback on an empty queue, or flushing"); }
GstV4l2Return gst_v4l2_allocator_stop (GstV4l2Allocator * allocator) { GstV4l2Object *obj = allocator->obj; struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory }; gint i = 0; GstV4l2Return ret = GST_V4L2_OK; GST_DEBUG_OBJECT (allocator, "stop allocator"); GST_OBJECT_LOCK (allocator); if (!g_atomic_int_get (&allocator->active)) goto done; if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) { GST_DEBUG_OBJECT (allocator, "allocator is still in use"); ret = GST_V4L2_BUSY; goto done; } while (gst_atomic_queue_pop (allocator->free_queue)) { /* nothing */ }; for (i = 0; i < allocator->count; i++) { GstV4l2MemoryGroup *group = allocator->groups[i]; allocator->groups[i] = NULL; if (group) gst_v4l2_memory_group_free (group); } /* Not all drivers support rebufs(0), so warn only */ if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0) GST_WARNING_OBJECT (allocator, "error releasing buffers buffers: %s", g_strerror (errno)); allocator->count = 0; g_atomic_int_set (&allocator->active, FALSE); done: GST_OBJECT_UNLOCK (allocator); return ret; }
/* Flush */ static void Flush( decoder_t *p_dec ) { decoder_sys_t *p_sys = p_dec->p_sys; GstBuffer *p_buffer; gboolean b_ret; /* Send a new segment event. Seeking position is * irrelevant in this case, as the main motive for a * seek here, is to tell the elements to start flushing * and start accepting buffers from a new time segment */ b_ret = gst_element_seek_simple( p_sys->p_decoder, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH, 0 ); msg_Dbg( p_dec, "new segment event : %d", b_ret ); /* flush the output buffers from the queue */ while( ( p_buffer = gst_atomic_queue_pop( p_sys->p_que ) ) ) gst_buffer_unref( p_buffer ); p_sys->b_prerolled = false; }
/* must be called with the lock */ static gboolean default_stop (GstBufferPool * pool) { GstBufferPoolPrivate *priv = pool->priv; GstBuffer *buffer; GstBufferPoolClass *pclass; pclass = GST_BUFFER_POOL_GET_CLASS (pool); /* clear the pool */ while ((buffer = gst_atomic_queue_pop (priv->queue))) { GST_LOG_OBJECT (pool, "freeing %p", buffer); gst_poll_read_control (priv->poll); if (G_LIKELY (pclass->free_buffer)) pclass->free_buffer (pool, buffer); } priv->cur_buffers = 0; return TRUE; }
static GstV4l2MemoryGroup * gst_v4l2_allocator_alloc (GstV4l2Allocator * allocator) { GstV4l2MemoryGroup *group; if (!g_atomic_int_get (&allocator->active)) return NULL; group = gst_atomic_queue_pop (allocator->free_queue); if (group == NULL) { if (allocator->can_allocate) { group = gst_v4l2_allocator_create_buf (allocator); /* Don't hammer on CREATE_BUFS */ if (group == NULL) allocator->can_allocate = FALSE; } } return group; }
/* must be called with the lock */ static gboolean default_stop (GstBufferPool * pool) { GstBufferPoolPrivate *priv = pool->priv; GstBuffer *buffer; /* clear the pool */ while ((buffer = gst_atomic_queue_pop (priv->queue))) { while (!gst_poll_read_control (priv->poll)) { if (errno == EWOULDBLOCK) { /* We put the buffer into the queue but did not finish writing control * yet, let's wait a bit and retry */ g_thread_yield (); continue; } else { /* Critical error but GstPoll already complained */ break; } } do_free_buffer (pool, buffer); } return priv->cur_buffers == 0; }
/** * gst_bus_timed_pop_filtered: * @bus: a #GstBus to pop from * @timeout: a timeout in nanoseconds, or GST_CLOCK_TIME_NONE to wait forever * @types: message types to take into account, GST_MESSAGE_ANY for any type * * Get a message from the bus whose type matches the message type mask @types, * waiting up to the specified timeout (and discarding any messages that do not * match the mask provided). * * If @timeout is 0, this function behaves like gst_bus_pop_filtered(). If * @timeout is #GST_CLOCK_TIME_NONE, this function will block forever until a * matching message was posted on the bus. * * Returns: (transfer full) (nullable): a #GstMessage matching the * filter in @types, or %NULL if no matching message was found on * the bus until the timeout expired. The message is taken from * the bus and needs to be unreffed with gst_message_unref() after * usage. * * MT safe. */ GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types) { GstMessage *message; GTimeVal now, then; gboolean first_round = TRUE; GstClockTime elapsed = 0; g_return_val_if_fail (GST_IS_BUS (bus), NULL); g_return_val_if_fail (types != 0, NULL); g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL); g_mutex_lock (&bus->priv->queue_lock); while (TRUE) { gint ret; GST_LOG_OBJECT (bus, "have %d messages", gst_atomic_queue_length (bus->priv->queue)); while ((message = gst_atomic_queue_pop (bus->priv->queue))) { if (bus->priv->poll) { while (!gst_poll_read_control (bus->priv->poll)) { if (errno == EWOULDBLOCK) { /* Retry, this can happen if pushing to the queue has finished, * popping here succeeded but writing control did not finish * before we got to this line. */ /* Give other threads the chance to do something */ g_thread_yield (); continue; } else { /* This is a real error and means that either the bus is in an * inconsistent state, or the GstPoll is invalid. GstPoll already * prints a critical warning about this, no need to do that again * ourselves */ break; } } } GST_DEBUG_OBJECT (bus, "got message %p, %s from %s, type mask is %u", message, GST_MESSAGE_TYPE_NAME (message), GST_MESSAGE_SRC_NAME (message), (guint) types); if ((GST_MESSAGE_TYPE (message) & types) != 0) { /* Extra check to ensure extended types don't get matched unless * asked for */ if ((!GST_MESSAGE_TYPE_IS_EXTENDED (message)) || (types & GST_MESSAGE_EXTENDED)) { /* exit the loop, we have a message */ goto beach; } } GST_DEBUG_OBJECT (bus, "discarding message, does not match mask"); gst_message_unref (message); message = NULL; } /* no need to wait, exit loop */ if (timeout == 0) break; else if (timeout != GST_CLOCK_TIME_NONE) { if (first_round) { g_get_current_time (&then); first_round = FALSE; } else { g_get_current_time (&now); elapsed = GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (then); if (elapsed > timeout) break; } } /* only here in timeout case */ g_assert (bus->priv->poll); g_mutex_unlock (&bus->priv->queue_lock); ret = gst_poll_wait (bus->priv->poll, timeout - elapsed); g_mutex_lock (&bus->priv->queue_lock); if (ret == 0) { GST_INFO_OBJECT (bus, "timed out, breaking loop"); break; } else { GST_INFO_OBJECT (bus, "we got woken up, recheck for message"); } } beach: g_mutex_unlock (&bus->priv->queue_lock); return message; }
/** * gst_bus_timed_pop_filtered: * @bus: a #GstBus to pop from * @timeout: a timeout in nanoseconds, or GST_CLOCK_TIME_NONE to wait forever * @types: message types to take into account, GST_MESSAGE_ANY for any type * * Get a message from the bus whose type matches the message type mask @types, * waiting up to the specified timeout (and discarding any messages that do not * match the mask provided). * * If @timeout is 0, this function behaves like gst_bus_pop_filtered(). If * @timeout is #GST_CLOCK_TIME_NONE, this function will block forever until a * matching message was posted on the bus. * * Returns: (transfer full) (nullable): a #GstMessage matching the * filter in @types, or %NULL if no matching message was found on * the bus until the timeout expired. The message is taken from * the bus and needs to be unreffed with gst_message_unref() after * usage. * * MT safe. */ GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types) { GstMessage *message; GTimeVal now, then; gboolean first_round = TRUE; GstClockTime elapsed = 0; g_return_val_if_fail (GST_IS_BUS (bus), NULL); g_return_val_if_fail (types != 0, NULL); g_return_val_if_fail (timeout == 0 || bus->priv->poll != NULL, NULL); g_mutex_lock (&bus->priv->queue_lock); while (TRUE) { gint ret; GST_LOG_OBJECT (bus, "have %d messages", gst_atomic_queue_length (bus->priv->queue)); while ((message = gst_atomic_queue_pop (bus->priv->queue))) { if (bus->priv->poll) gst_poll_read_control (bus->priv->poll); GST_DEBUG_OBJECT (bus, "got message %p, %s from %s, type mask is %u", message, GST_MESSAGE_TYPE_NAME (message), GST_MESSAGE_SRC_NAME (message), (guint) types); if ((GST_MESSAGE_TYPE (message) & types) != 0) { /* Extra check to ensure extended types don't get matched unless * asked for */ if ((!GST_MESSAGE_TYPE_IS_EXTENDED (message)) || (types & GST_MESSAGE_EXTENDED)) { /* exit the loop, we have a message */ goto beach; } } GST_DEBUG_OBJECT (bus, "discarding message, does not match mask"); gst_message_unref (message); message = NULL; } /* no need to wait, exit loop */ if (timeout == 0) break; else if (timeout != GST_CLOCK_TIME_NONE) { if (first_round) { g_get_current_time (&then); first_round = FALSE; } else { g_get_current_time (&now); elapsed = GST_TIMEVAL_TO_TIME (now) - GST_TIMEVAL_TO_TIME (then); if (elapsed > timeout) break; } } /* only here in timeout case */ g_assert (bus->priv->poll); g_mutex_unlock (&bus->priv->queue_lock); ret = gst_poll_wait (bus->priv->poll, timeout - elapsed); g_mutex_lock (&bus->priv->queue_lock); if (ret == 0) { GST_INFO_OBJECT (bus, "timed out, breaking loop"); break; } else { GST_INFO_OBJECT (bus, "we got woken up, recheck for message"); } } beach: g_mutex_unlock (&bus->priv->queue_lock); return message; }
static GstFlowReturn default_acquire_buffer (GstBufferPool * pool, GstBuffer ** buffer, GstBufferPoolAcquireParams * params) { GstFlowReturn result; GstBufferPoolPrivate *priv = pool->priv; while (TRUE) { if (G_UNLIKELY (GST_BUFFER_POOL_IS_FLUSHING (pool))) goto flushing; /* try to get a buffer from the queue */ *buffer = gst_atomic_queue_pop (priv->queue); if (G_LIKELY (*buffer)) { while (!gst_poll_read_control (priv->poll)) { if (errno == EWOULDBLOCK) { /* We put the buffer into the queue but did not finish writing control * yet, let's wait a bit and retry */ g_thread_yield (); continue; } else { /* Critical error but GstPoll already complained */ break; } } result = GST_FLOW_OK; GST_LOG_OBJECT (pool, "acquired buffer %p", *buffer); break; } /* no buffer, try to allocate some more */ GST_LOG_OBJECT (pool, "no buffer, trying to allocate"); result = do_alloc_buffer (pool, buffer, params); if (G_LIKELY (result == GST_FLOW_OK)) /* we have a buffer, return it */ break; if (G_UNLIKELY (result != GST_FLOW_EOS)) /* something went wrong, return error */ break; /* check if we need to wait */ if (params && (params->flags & GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT)) { GST_LOG_OBJECT (pool, "no more buffers"); break; } /* now we release the control socket, we wait for a buffer release or * flushing */ if (!gst_poll_read_control (pool->priv->poll)) { if (errno == EWOULDBLOCK) { /* This means that we have two threads trying to allocate buffers * already, and the other one already got the wait token. This * means that we only have to wait for the poll now and not write the * token afterwards: we will be woken up once the other thread is * woken up and that one will write the wait token it removed */ GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); } else { /* This is a critical error, GstPoll already gave a warning */ result = GST_FLOW_ERROR; break; } } else { /* We're the first thread waiting, we got the wait token and have to * write it again later * OR * We're a second thread and just consumed the flush token and block all * other threads, in which case we must not wait and give it back * immediately */ if (!GST_BUFFER_POOL_IS_FLUSHING (pool)) { GST_LOG_OBJECT (pool, "waiting for free buffers or flushing"); gst_poll_wait (priv->poll, GST_CLOCK_TIME_NONE); } gst_poll_write_control (pool->priv->poll); } } return result; /* ERRORS */ flushing: { GST_DEBUG_OBJECT (pool, "we are flushing"); return GST_FLOW_FLUSHING; } }
/* Close the decoder instance */ static void CloseDecoder( vlc_object_t *p_this ) { decoder_t *p_dec = ( decoder_t* )p_this; decoder_sys_t *p_sys = p_dec->p_sys; gboolean b_running = p_sys->b_running; if( b_running ) { GstMessage *p_msg; GstFlowReturn i_ret; p_sys->b_running = false; /* Send EOS to the pipeline */ i_ret = gst_app_src_end_of_stream( GST_APP_SRC_CAST( p_sys->p_decode_src )); msg_Dbg( p_dec, "app src eos: %s", gst_flow_get_name( i_ret ) ); /* and catch it on the bus with a timeout */ p_msg = gst_bus_timed_pop_filtered( p_sys->p_bus, 2000000000ULL, GST_MESSAGE_EOS | GST_MESSAGE_ERROR ); if( p_msg ) { switch( GST_MESSAGE_TYPE( p_msg ) ){ case GST_MESSAGE_EOS: msg_Dbg( p_dec, "got eos" ); break; default: p_dec->b_error = default_msg_handler( p_dec, p_msg ); if( p_dec->b_error ) msg_Err( p_dec, "pipeline may not close gracefully" ); break; } gst_message_unref( p_msg ); } else msg_Warn( p_dec, "no message, pipeline may not close gracefully" ); } /* Remove any left-over buffers from the queue */ if( p_sys->p_que ) { GstBuffer *p_buf; while( ( p_buf = gst_atomic_queue_pop( p_sys->p_que ) ) ) gst_buffer_unref( p_buf ); gst_atomic_queue_unref( p_sys->p_que ); } if( b_running && gst_element_set_state( p_sys->p_decoder, GST_STATE_NULL ) != GST_STATE_CHANGE_SUCCESS ) msg_Err( p_dec, "failed to change the state to NULL," \ "pipeline may not close gracefully" ); if( p_sys->p_allocator ) gst_object_unref( p_sys->p_allocator ); if( p_sys->p_bus ) gst_object_unref( p_sys->p_bus ); if( p_sys->p_decode_src ) gst_object_unref( p_sys->p_decode_src ); if( p_sys->p_decode_in ) gst_object_unref( p_sys->p_decode_in ); if( p_sys->p_decode_out ) gst_object_unref( p_sys->p_decode_out ); if( p_sys->p_decoder ) gst_object_unref( p_sys->p_decoder ); free( p_sys ); }
/* Decode */ static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block ) { block_t *p_block; picture_t *p_pic = NULL; decoder_sys_t *p_sys = p_dec->p_sys; GstMessage *p_msg; GstBuffer *p_buf; if( !pp_block ) return NULL; p_block = *pp_block; if( !p_block ) goto check_messages; if( unlikely( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED ) ) ) { if( p_block->i_flags & BLOCK_FLAG_DISCONTINUITY ) Flush( p_dec ); if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) { block_Release( p_block ); goto done; } } if( likely( p_block->i_buffer ) ) { p_buf = gst_buffer_new_wrapped_full( GST_MEMORY_FLAG_READONLY, p_block->p_start, p_block->i_size, p_block->p_buffer - p_block->p_start, p_block->i_buffer, p_block, ( GDestroyNotify )block_Release ); if( unlikely( p_buf == NULL ) ) { msg_Err( p_dec, "failed to create input gstbuffer" ); p_dec->b_error = true; block_Release( p_block ); goto done; } if( p_block->i_dts > VLC_TS_INVALID ) GST_BUFFER_DTS( p_buf ) = gst_util_uint64_scale( p_block->i_dts, GST_SECOND, GST_MSECOND ); if( p_block->i_pts <= VLC_TS_INVALID ) GST_BUFFER_PTS( p_buf ) = GST_BUFFER_DTS( p_buf ); else GST_BUFFER_PTS( p_buf ) = gst_util_uint64_scale( p_block->i_pts, GST_SECOND, GST_MSECOND ); if( p_block->i_length > VLC_TS_INVALID ) GST_BUFFER_DURATION( p_buf ) = gst_util_uint64_scale( p_block->i_length, GST_SECOND, GST_MSECOND ); if( p_dec->fmt_in.video.i_frame_rate && p_dec->fmt_in.video.i_frame_rate_base ) GST_BUFFER_DURATION( p_buf ) = gst_util_uint64_scale( GST_SECOND, p_dec->fmt_in.video.i_frame_rate_base, p_dec->fmt_in.video.i_frame_rate ); /* Give the input buffer to GStreamer Bin. * * libvlc libvlc * \ (i/p) (o/p) ^ * \ / * ___v____GSTREAMER BIN_____/____ * | | * | appsrc-->decode-->vlcsink | * |_______________________________| * * * * * * * * * * * * * * * * * * * * * */ if( unlikely( gst_app_src_push_buffer( GST_APP_SRC_CAST( p_sys->p_decode_src ), p_buf ) != GST_FLOW_OK ) ) { /* block will be released internally, * when gst_buffer_unref() is called */ p_dec->b_error = true; msg_Err( p_dec, "failed to push buffer" ); goto done; } } else block_Release( p_block ); check_messages: /* Poll for any messages, errors */ p_msg = gst_bus_pop_filtered( p_sys->p_bus, GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_WARNING | GST_MESSAGE_INFO ); if( p_msg ) { switch( GST_MESSAGE_TYPE( p_msg ) ){ case GST_MESSAGE_EOS: /* for debugging purpose */ msg_Warn( p_dec, "got unexpected eos" ); break; /* First buffer received */ case GST_MESSAGE_ASYNC_DONE: /* for debugging purpose */ p_sys->b_prerolled = true; msg_Dbg( p_dec, "Pipeline is prerolled" ); break; default: p_dec->b_error = default_msg_handler( p_dec, p_msg ); if( p_dec->b_error ) { gst_message_unref( p_msg ); goto done; } break; } gst_message_unref( p_msg ); } /* Look for any output buffers in the queue */ if( gst_atomic_queue_peek( p_sys->p_que ) ) { GstBuffer *p_buf = GST_BUFFER_CAST( gst_atomic_queue_pop( p_sys->p_que )); GstMemory *p_mem; if(( p_mem = gst_buffer_peek_memory( p_buf, 0 )) && GST_IS_VLC_PICTURE_PLANE_ALLOCATOR( p_mem->allocator )) { p_pic = picture_Hold(( (GstVlcPicturePlane*) p_mem )->p_pic ); } else { GstVideoFrame frame; /* Get a new picture */ p_pic = decoder_NewPicture( p_dec ); if( !p_pic ) goto done; if( unlikely( !gst_video_frame_map( &frame, &p_sys->vinfo, p_buf, GST_MAP_READ ) ) ) { msg_Err( p_dec, "failed to map gst video frame" ); gst_buffer_unref( p_buf ); p_dec->b_error = true; goto done; } gst_CopyPicture( p_pic, &frame ); gst_video_frame_unmap( &frame ); } if( likely( GST_BUFFER_PTS_IS_VALID( p_buf ) ) ) p_pic->date = gst_util_uint64_scale( GST_BUFFER_PTS( p_buf ), GST_MSECOND, GST_SECOND ); else msg_Warn( p_dec, "Gst Buffer has no timestamp" ); gst_buffer_unref( p_buf ); } done: *pp_block = NULL; return p_pic; }