Fix seek issue 2.0 accepted/2.0/20130422.163146 submit/2.0/20130420.083526
authorZhao Halley <halley.zhao@intel.com>
Sat, 20 Apr 2013 05:12:41 +0000 (13:12 +0800)
committerZhao Halley <halley.zhao@intel.com>
Sat, 20 Apr 2013 08:21:39 +0000 (16:21 +0800)
https://bugs.tizen.org/jira/browse/TIVI-708

[Reason]
When decoder runs faster than downlink elements (postprocessing and
sink), many video surface are cached in pools (queue element for example).
Then decoding will fail because of no video surface available.

During seek, data flows much fater to position to dedicted frame.
It is easier to run into to above failure.

[Resolution]
1. A condition wait is added to decode func, it waits up until a surface is available.
   It usually happens when a surface is recycled after use (renders to Window/Pixmap).
2. The start of seek event (GST_EVENT_FLUSH_START) will also wake up the above wait,
   even if there is still no surface available.
   The end of seek event (GST_EVENT_FLUSH_STOP) will disable such special case.

to assist the above mechanism
3. The resumed decode func trigger retry on the previous video data.

by the way,
1. The previous solution bases on a timeout wait, it isn't
   reliable during quick seek.
2. Gst event and status change is added in GST_INFO to ease debug.
   If you still meet seek issue, please send us the log wiht
   "export GST_DEBUG=vaapidecode:3"

gst-libs/gst/vaapi/gstvaapidecoder.c [changed mode: 0644->0755]
gst-libs/gst/vaapi/gstvaapidecoder.h [changed mode: 0644->0755]
gst/vaapi/gstvaapidecode.c [changed mode: 0644->0755]
gst/vaapi/gstvaapidecode.h [changed mode: 0644->0755]
tests/test-decode.c [changed mode: 0644->0755]
tests/test-subpicture.c [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 0df566d..489ae2f
@@ -90,7 +90,7 @@ pop_buffer(GstVaapiDecoder *decoder)
 }
 
 static GstVaapiDecoderStatus
-decode_step(GstVaapiDecoder *decoder)
+decode_step(GstVaapiDecoder *decoder, gboolean *try_again)
 {
     GstVaapiDecoderStatus status;
     GstBuffer *buffer;
@@ -101,7 +101,15 @@ decode_step(GstVaapiDecoder *decoder)
         return status;
 
     do {
-        buffer = pop_buffer(decoder);
+        if (*try_again){
+            buffer = gst_buffer_new();
+            if (buffer){
+                gst_buffer_set_data(buffer, NULL, 0);
+            }
+            *try_again = FALSE;
+        }else{
+            buffer = pop_buffer(decoder);
+        }
         if (!buffer)
             return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
 
@@ -413,6 +421,7 @@ gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
  * gst_vaapi_decoder_get_surface:
  * @decoder: a #GstVaapiDecoder
  * @pstatus: return location for the decoder status, or %NULL
+ * @try_again: a #gboolean
  *
  * Flushes encoded buffers to the decoder and returns a decoded
  * surface, if any.
@@ -424,7 +433,8 @@ gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf)
 GstVaapiSurfaceProxy *
 gst_vaapi_decoder_get_surface(
     GstVaapiDecoder       *decoder,
-    GstVaapiDecoderStatus *pstatus
+    GstVaapiDecoderStatus *pstatus,
+    gboolean              try_again
 )
 {
     GstVaapiSurfaceProxy *proxy;
@@ -438,7 +448,7 @@ gst_vaapi_decoder_get_surface(
     proxy = pop_surface(decoder);
     if (!proxy) {
         do {
-            status = decode_step(decoder);
+            status = decode_step(decoder, &try_again);
         } while (status == GST_VAAPI_DECODER_STATUS_SUCCESS);
         proxy = pop_surface(decoder);
     }
old mode 100644 (file)
new mode 100755 (executable)
index ebcead9..0ebb92b
@@ -129,7 +129,8 @@ gst_vaapi_decoder_put_buffer(GstVaapiDecoder *decoder, GstBuffer *buf);
 GstVaapiSurfaceProxy *
 gst_vaapi_decoder_get_surface(
     GstVaapiDecoder       *decoder,
-    GstVaapiDecoderStatus *pstatus
+    GstVaapiDecoderStatus *pstatus,
+    gboolean              try_again
 );
 
 void
old mode 100644 (file)
new mode 100755 (executable)
index 309348e..0f00a53
@@ -203,29 +203,27 @@ gst_vaapidecode_step(GstVaapiDecode *decode)
     GstBuffer *buffer;
     GstFlowReturn ret;
     GstClockTime timestamp;
-    gint64 end_time;
+    gboolean try_again = FALSE;
 
     for (;;) {
-        end_time = decode->render_time_base;
-        if (!end_time)
-            end_time = g_get_monotonic_time();
-        end_time += GST_TIME_AS_USECONDS(decode->last_buffer_time);
-        end_time += G_TIME_SPAN_SECOND;
-
-        proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status);
+        proxy = gst_vaapi_decoder_get_surface(decode->decoder, &status, try_again);
+        try_again = FALSE;
         if (!proxy) {
             if (status == GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE) {
-                gboolean was_signalled;
                 g_mutex_lock(&decode->decoder_mutex);
-                was_signalled = g_cond_wait_until(
+                if (decode->escape_decoding){
+                    goto handle_escape_decoding;
+                }
+                g_cond_wait(
                     &decode->decoder_ready,
-                    &decode->decoder_mutex,
-                    end_time
+                    &decode->decoder_mutex
                 );
+                if (decode->escape_decoding){
+                    goto handle_escape_decoding;
+                }
                 g_mutex_unlock(&decode->decoder_mutex);
-                if (was_signalled)
-                    continue;
-                goto error_decode_timeout;
+                try_again = TRUE;
+                continue;
             }
             if (status != GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA)
                 goto error_decode;
@@ -275,10 +273,10 @@ gst_vaapidecode_step(GstVaapiDecode *decode)
     return GST_FLOW_OK;
 
     /* ERRORS */
-error_decode_timeout:
+handle_escape_decoding:
     {
-        GST_DEBUG("decode timeout. Decoder required a VA surface but none "
-                  "got available within one second");
+        g_mutex_unlock(&decode->decoder_mutex);
+        GST_INFO("Escape decoding wait: flush event comes for seek etc.");
         return GST_FLOW_UNEXPECTED;
     }
 error_decode:
@@ -518,12 +516,32 @@ gst_vaapidecode_finalize(GObject *object)
     G_OBJECT_CLASS(gst_vaapidecode_parent_class)->finalize(object);
 }
 
+typedef struct {
+    int trans;
+    char *trans_name;
+} _GstStateChangeMap;
+_GstStateChangeMap gst_state_change_string_map[] = {
+    {GST_STATE_CHANGE_NULL_TO_READY,    "GST_STATE_CHANGE_NULL_TO_READY"}, 
+    {GST_STATE_CHANGE_READY_TO_PAUSED,  "GST_STATE_CHANGE_READY_TO_PAUSED",},
+    {GST_STATE_CHANGE_PAUSED_TO_PLAYING,"GST_STATE_CHANGE_PAUSED_TO_PLAYING"},
+    {GST_STATE_CHANGE_PLAYING_TO_PAUSED,"GST_STATE_CHANGE_PLAYING_TO_PAUSED"},
+    {GST_STATE_CHANGE_PAUSED_TO_READY,  "GST_STATE_CHANGE_PAUSED_TO_READY"},
+    {GST_STATE_CHANGE_READY_TO_NULL,    "GST_STATE_CHANGE_READY_TO_NULL"},
+    {-1,                                "UNDEFINED_STATE_CHANGE"}
+};
+
 static GstStateChangeReturn
 gst_vaapidecode_change_state(GstElement *element, GstStateChange transition)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(element);
     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+    int i;
 
+    for (i=0; i<sizeof(gst_state_change_string_map)/sizeof(_GstStateChangeMap);i++) {
+        if (gst_state_change_string_map[i].trans == transition)
+            GST_INFO(gst_state_change_string_map[i].trans_name);
+    }
+    
     switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
         decode->is_ready = TRUE;
@@ -712,11 +730,20 @@ gst_vaapidecode_sink_event(GstPad *pad, GstEvent *event)
 {
     GstVaapiDecode * const decode = GST_VAAPIDECODE(GST_OBJECT_PARENT(pad));
 
-    GST_DEBUG("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
+    GST_INFO("handle sink event '%s'", GST_EVENT_TYPE_NAME(event));
 
     /* Propagate event downstream */
     switch (GST_EVENT_TYPE(event)) {
+    case GST_EVENT_FLUSH_START:
+        g_mutex_lock(&decode->decoder_mutex);
+        decode->escape_decoding = TRUE;
+        g_cond_signal(&decode->decoder_ready);
+        g_mutex_unlock(&decode->decoder_mutex);
+        break;
     case GST_EVENT_FLUSH_STOP:
+        g_mutex_lock(&decode->decoder_mutex);
+        decode->escape_decoding = FALSE;
+        g_mutex_unlock(&decode->decoder_mutex);
         gst_segment_init(&decode->segment, GST_FORMAT_UNDEFINED);
         if (decode->decoder)
             gst_vaapi_decoder_clear_buffer(decode->decoder);
@@ -784,6 +811,7 @@ gst_vaapidecode_init(GstVaapiDecode *decode)
     decode->render_time_base    = 0;
     decode->last_buffer_time    = 0;
     decode->is_ready            = FALSE;
+    decode->escape_decoding          = FALSE;
 
     g_mutex_init(&decode->decoder_mutex);
     g_cond_init(&decode->decoder_ready);
old mode 100644 (file)
new mode 100755 (executable)
index c8b2583..ff27780
@@ -76,6 +76,8 @@ struct _GstVaapiDecode {
     gint64              render_time_base;
     GstClockTime        last_buffer_time;
     unsigned int        is_ready        : 1;
+    // escape decoding (blocked by hw resource) when there is _flush event, usually happens during seek.
+    unsigned int        escape_decoding : 1;
 };
 
 struct _GstVaapiDecodeClass {
old mode 100644 (file)
new mode 100755 (executable)
index f02e612..e173aaa
@@ -181,7 +181,7 @@ main(int argc, char *argv[])
     if (!gst_vaapi_decoder_put_buffer(decoder, NULL))
         g_error("could not send EOS to the decoder");
 
-    proxy = gst_vaapi_decoder_get_surface(decoder, &status);
+    proxy = gst_vaapi_decoder_get_surface(decoder, &status, FALSE);
     if (!proxy)
         g_error("could not get decoded surface (decoder status %d)", status);
 
old mode 100644 (file)
new mode 100755 (executable)
index 7a65fe3..b9eae9a
@@ -157,7 +157,7 @@ main(int argc, char *argv[])
     if (!gst_vaapi_decoder_put_buffer(decoder, NULL))
         g_error("could not send EOS to the decoder");
 
-    proxy = gst_vaapi_decoder_get_surface(decoder, &status);
+    proxy = gst_vaapi_decoder_get_surface(decoder, &status, FALSE);
     if (!proxy)
         g_error("could not get decoded surface (decoder status %d)", status);