/***********************************************************************************
 * Internal functions
 ***********************************************************************************/
static void hls_progress_buffer_flush_data(HLSProgressBuffer *element)
{
    guint i = 0;

    g_mutex_lock(element->lock);

    element->srcresult = GST_FLOW_WRONG_STATE;

    g_cond_signal(element->add_cond);
    g_cond_signal(element->del_cond);

    element->cache_write_index = -1;
    element->cache_read_index = 0;
    for (i = 0; i < NUM_OF_CACHED_SEGMENTS; i++)
    {
        if (element->cache[i])
        {
            cache_set_write_position(element->cache[i], 0);
            cache_set_read_position(element->cache[i], 0);
            element->cache_size[i] = 0;
            element->cache_write_ready[i] = TRUE;
        }
    }

    g_mutex_unlock(element->lock);
}
Ejemplo n.º 2
0
/***********************************************************************************
 * Seek implementation
 ***********************************************************************************/
static gboolean progress_buffer_perform_push_seek(ProgressBuffer *element, GstPad *pad, GstEvent *event)
{
    GstFormat    format;
    gdouble      rate;
    GstSeekFlags flags;
    GstSeekType  start_type, stop_type;
    gint64       position;
    GstSegment   segment;

    gst_event_parse_seek(event, &rate, &format, &flags, &start_type, &position, &stop_type, NULL);

    if (format != GST_FORMAT_BYTES || start_type != GST_SEEK_TYPE_SET)
        return FALSE;

    if (stop_type != GST_SEEK_TYPE_NONE)
    {
        gst_element_message_full(GST_ELEMENT(element),
            GST_MESSAGE_WARNING,
            GST_CORE_ERROR,
            GST_CORE_ERROR_SEEK, g_strdup("stop_type != GST_SEEK_TYPE_NONE. Seeking to stop is not supported."), NULL,
            ("progressbuffer.c"), ("progress_buffer_perform_push_seek"), 0);
        return FALSE;
    }

    if (flags & GST_SEEK_FLAG_FLUSH)
        gst_pad_push_event(pad, gst_event_new_flush_start());

    // Signal the task to stop if it's waiting.
    g_mutex_lock(&element->lock);
    element->srcresult = GST_FLOW_FLUSHING;
    g_cond_signal(&element->add_cond);
    g_mutex_unlock(&element->lock);

    GST_PAD_STREAM_LOCK(pad); // Wait for task to stop

    g_mutex_lock(&element->lock);
    element->srcresult = GST_FLOW_OK;

#ifdef ENABLE_SOURCE_SEEKING
    element->instant_seek = (position >= element->sink_segment.start &&
                             (position - (gint64)element->sink_segment.position) <= element->bandwidth * element->wait_tolerance);

    if (element->instant_seek)
    {
        cache_set_read_position(element->cache, position - element->cache_read_offset);
        gst_segment_init(&segment, GST_FORMAT_BYTES);
        segment.rate = rate;
        segment.start = position;
        segment.stop = element->sink_segment.stop;
        segment.position = position;
        progress_buffer_set_pending_event(element, gst_event_new_segment(&segment));
    }
    else
    {
        // Clear any pending events, since we doing seek.
        reset_eos(element, TRUE);
    }
#else
    cache_set_read_position(element->cache, position - element->cache_read_offset);
    gst_segment_init(&segment, GST_FORMAT_BYTES);
    segment.rate = rate;
    segment.start = position;
    segment.stop = element->sink_segment.stop;
    segment.position = position;
    progress_buffer_set_pending_event(element, gst_event_new_segment(&segment));
#endif

    g_mutex_unlock(&element->lock);

#ifdef ENABLE_SOURCE_SEEKING
    if (!element->instant_seek)
    {
        element->is_source_seeking = TRUE;
        if (!gst_pad_push_event(element->sinkpad, gst_event_new_seek(rate, GST_FORMAT_BYTES, flags, GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_NONE, 0)))
        {
            element->instant_seek = TRUE;
            cache_set_read_position(element->cache, position - element->cache_read_offset);
            gst_segment_init(&segment, GST_FORMAT_BYTES);
            segment.rate = rate;
            segment.start = position;
            segment.stop = element->sink_segment.stop;
            segment.position = position;
            progress_buffer_set_pending_event(element, gst_event_new_segment(&segment));
        }
        element->is_source_seeking = FALSE;
    }
#endif

    if (flags & GST_SEEK_FLAG_FLUSH)
        gst_pad_push_event(pad, gst_event_new_flush_stop(TRUE));

    gst_pad_start_task(element->srcpad, progress_buffer_loop, element, NULL);
    GST_PAD_STREAM_UNLOCK(pad);

// INLINE - gst_event_unref()
    gst_event_unref(event);
    return TRUE;
}
Ejemplo n.º 3
0
/**
 * progress_buffer_enqueue_item()
 *
 * Add an item in the queue. Must be called in the locked context.  Item may be event or data.
 */
static GstFlowReturn progress_buffer_enqueue_item(ProgressBuffer *element, GstMiniObject *item)
{
    gboolean signal = FALSE;

    if (GST_IS_BUFFER (item))
    {
        gdouble elapsed;
        // update sink segment position
        element->sink_segment.position = GST_BUFFER_OFFSET(GST_BUFFER(item)) + gst_buffer_get_size (GST_BUFFER(item));

        if(element->sink_segment.stop < element->sink_segment.position) // This must never happen.
            return  GST_FLOW_ERROR;

        cache_write_buffer(element->cache, GST_BUFFER(item));

        elapsed = g_timer_elapsed(element->bandwidth_timer, NULL);
        element->subtotal += gst_buffer_get_size (GST_BUFFER(item));

        if (elapsed > 1.0)
        {
            element->bandwidth = element->subtotal/elapsed;
            element->subtotal = 0;
            g_timer_start(element->bandwidth_timer);
        }

        // send buffer progress position up (used to track buffer fill, etc.)
        signal = send_position_message(element, signal);
    }
    else if (GST_IS_EVENT (item))
    {
        GstEvent *event = GST_EVENT_CAST (item);

        switch (GST_EVENT_TYPE (event))
        {
            case GST_EVENT_EOS:
                element->eos_status.eos = TRUE;
                if (element->sink_segment.position < element->sink_segment.stop)
                    element->sink_segment.stop = element->sink_segment.position;

                progress_buffer_set_pending_event(element, NULL);

                signal = send_position_message(element, TRUE);
                gst_event_unref(event); // INLINE - gst_event_unref()
                break;

            case GST_EVENT_SEGMENT:
            {
                GstSegment segment;

                element->unexpected = FALSE;

                gst_event_copy_segment (event, &segment);

                if (segment.format != GST_FORMAT_BYTES)
                {
                    gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_STREAM_ERROR, GST_STREAM_ERROR_FORMAT,
                                             g_strdup("GST_FORMAT_BYTES buffers expected."), NULL,
                                             ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0);
                    gst_event_unref(event); // INLINE - gst_event_unref()
                    return GST_FLOW_ERROR;
                 }

                if (segment.stop - segment.start <= 0)
                {
                    gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE,
                                             g_strdup("Only limited content is supported by progressbuffer."), NULL,
                                             ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0);
                    gst_event_unref(event); // INLINE - gst_event_unref()
                    return GST_FLOW_ERROR;
                }

                if ((segment.flags & GST_SEGMENT_FLAG_UPDATE) == GST_SEGMENT_FLAG_UPDATE) // Updating segments create new cache.
                {
                    if (element->cache)
                        destroy_cache(element->cache);

                    element->cache = create_cache();
                    if (!element->cache)
                    {
                        gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_RESOURCE_ERROR, GST_RESOURCE_ERROR_OPEN_READ_WRITE,
                                                 g_strdup("Couldn't create backing cache"), NULL,
                                                 ("progressbuffer.c"), ("progress_buffer_enqueue_item"), 0);
                        gst_event_unref(event); // INLINE - gst_event_unref()
                        return GST_FLOW_ERROR;
                    }
                }
                else
                {
                    cache_set_write_position(element->cache, 0);
                    cache_set_read_position(element->cache, 0);
                    element->cache_read_offset = segment.start;
                }

                gst_segment_copy_into (&segment, &element->sink_segment);
                progress_buffer_set_pending_event(element, event);
                element->instant_seek = TRUE;

                signal = send_position_message(element, TRUE);
                break;
            }

            default:
                gst_event_unref(event); // INLINE - gst_event_unref()
                break;
        }
    }

    if (signal)
        g_cond_signal(&element->add_cond);

    return GST_FLOW_OK;
}
/**
 * hls_progress_buffer_sink_event()
 *
 * Receives event from the sink pad (currently, data from javasource).  When an event comes in,
 * we get the data from the pad by getting at the ProgressBuffer* object associated with the pad.
 */
static gboolean hls_progress_buffer_sink_event(GstPad *pad, GstEvent *event)
{
    HLSProgressBuffer *element = HLS_PROGRESS_BUFFER(GST_PAD_PARENT(pad));
    gboolean ret = FALSE;

    switch (GST_EVENT_TYPE (event))
    {
    case GST_EVENT_NEWSEGMENT:
        {
            gboolean update;
            GstFormat format;
            gdouble rate, arate;
            gint64 start, stop, time;

            g_mutex_lock(element->lock);
            if (element->srcresult != GST_FLOW_OK)
            {
                // INLINE - gst_event_unref()
                gst_event_unref(event);
                g_mutex_unlock(element->lock);
                return TRUE;
            }
            g_mutex_unlock(element->lock);

            if (element->is_eos)
            {
                element->is_eos = FALSE;
                element->srcresult = GST_FLOW_OK;
                if (gst_pad_is_linked(element->srcpad))
                    gst_pad_start_task(element->srcpad, hls_progress_buffer_loop, element);
            }

            // In HLS mode javasource will set time to correct position in time unit, even if segment in byte units.
            // Maybe not perfect, but works.
            gst_event_parse_new_segment_full(event, &update, &rate, &arate, &format, &start, &stop, &time);
            // INLINE - gst_event_unref()
            gst_event_unref(event);
            ret = TRUE;

            if (stop - start <= 0)
            {
                gst_element_message_full(GST_ELEMENT(element), GST_MESSAGE_ERROR, GST_STREAM_ERROR, GST_STREAM_ERROR_WRONG_TYPE, g_strdup("Only limited content is supported by hlsprogressbuffer."), NULL, ("hlsprogressbuffer.c"), ("hls_progress_buffer_src_event"), 0);
                return TRUE;
            }

            if (element->send_new_segment)
            {
                event = gst_event_new_new_segment(update, rate, GST_FORMAT_TIME, 0, -1, time);
                element->send_new_segment = FALSE;
                ret = gst_pad_push_event(element->srcpad, event);
            }

            // Get and prepare next write segment
            g_mutex_lock(element->lock);
            element->cache_write_index = (element->cache_write_index + 1) % NUM_OF_CACHED_SEGMENTS;

            while (element->srcresult == GST_FLOW_OK && !element->cache_write_ready[element->cache_write_index])
            {
                g_mutex_unlock(element->lock);
                send_hls_full_message(element);
                g_mutex_lock(element->lock);
                g_cond_wait(element->del_cond, element->lock);
                if (element->srcresult != GST_FLOW_OK)
                {
                    g_mutex_unlock(element->lock);
                    return TRUE;
                }
            }
            element->cache_size[element->cache_write_index] = stop;
            element->cache_write_ready[element->cache_write_index] = FALSE;
            cache_set_write_position(element->cache[element->cache_write_index], 0);
            cache_set_read_position(element->cache[element->cache_write_index], 0);

            g_mutex_unlock(element->lock);

            send_hls_resume_message(element); // Send resume message for each segment
        }
        break;
    case GST_EVENT_FLUSH_START:
        g_mutex_lock(element->lock);
        element->is_flushing = TRUE;
        g_mutex_unlock(element->lock);

        ret = gst_pad_push_event(element->srcpad, event);
        hls_progress_buffer_flush_data(element);

        if (gst_pad_is_linked(element->srcpad))
            gst_pad_pause_task(element->srcpad);

        break;
    case GST_EVENT_FLUSH_STOP:
        ret = gst_pad_push_event(element->srcpad, event);

        g_mutex_lock(element->lock);

        element->send_new_segment = TRUE;
        element->is_flushing = FALSE;
        element->srcresult = GST_FLOW_OK;
        
        if (!element->is_eos && gst_pad_is_linked(element->srcpad))
            gst_pad_start_task(element->srcpad, hls_progress_buffer_loop, element);

        g_mutex_unlock(element->lock);

        break;
    case GST_EVENT_EOS:
        send_hls_eos_message(element); // Just in case we stall

        g_mutex_lock(element->lock);
        element->is_eos = TRUE;
        g_cond_signal(element->add_cond);
        g_mutex_unlock(element->lock);
        // INLINE - gst_event_unref()
        gst_event_unref(event);
        ret = TRUE;

        break;
    default:
        ret = gst_pad_push_event(element->srcpad, event);
        break;
    }

    return ret;
}