After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 736655 - basesink: preroll issue for some clips which audio is shorter than video
basesink: preroll issue for some clips which audio is shorter than video
Status: RESOLVED OBSOLETE
Product: GStreamer
Classification: Platform
Component: gstreamer (core)
git master
Other Linux
: Normal normal
: git master
Assigned To: GStreamer Maintainers
GStreamer Maintainers
: 726854 727074 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2014-09-15 03:14 UTC by kevin
Modified: 2018-11-03 12:23 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
basesink need call do_preroll when received GAP event. (1.57 KB, patch)
2014-09-17 09:44 UTC, kevin
none Details | Review
streamsynchronizer need send GAP event when state change from PLAYING to PAUSE. (1.56 KB, patch)
2014-09-17 09:45 UTC, kevin
none Details | Review
patch to fix the issue. (9.16 KB, patch)
2015-01-12 08:23 UTC, kevin
none Details | Review
Patch for item b) (2.83 KB, patch)
2015-01-12 09:52 UTC, kevin
none Details | Review
update the patch for minor change. (10.48 KB, patch)
2015-01-13 04:26 UTC, kevin
needs-work Details | Review
update the patch (2.84 KB, patch)
2015-01-13 04:26 UTC, kevin
needs-work Details | Review
Updated patch based on review. (10.25 KB, patch)
2015-01-20 06:08 UTC, kevin
none Details | Review
Updated patch based on review. (2.83 KB, patch)
2015-01-20 06:09 UTC, kevin
none Details | Review
Update based on IRC talk. (10.45 KB, patch)
2015-01-21 06:26 UTC, kevin
none Details | Review
Update based on IRC talk. (2.86 KB, patch)
2015-01-21 06:27 UTC, kevin
none Details | Review
Update based on IRC talk. (10.38 KB, patch)
2015-01-23 17:05 UTC, kevin
none Details | Review
Unit test for streamsynchronizer handle eos. (5.15 KB, patch)
2015-01-23 17:08 UTC, kevin
none Details | Review
Update patch for patch format (10.38 KB, patch)
2015-01-24 15:03 UTC, kevin
none Details | Review
Update patch for patch format (2.86 KB, patch)
2015-01-24 15:03 UTC, kevin
none Details | Review
Send GAP event in event thread. (10.38 KB, patch)
2015-01-26 03:26 UTC, kevin
needs-work Details | Review
Don't send GAP event in chain function. (2.86 KB, patch)
2015-01-26 03:27 UTC, kevin
needs-work Details | Review
Unit test for streamsynchronizer handle eos. (6.77 KB, patch)
2015-01-26 03:28 UTC, kevin
needs-work Details | Review
streamsynchronizer: Send GAP event to finish preroll when change state from PLAYING to PAUSED (11.12 KB, patch)
2015-02-15 09:10 UTC, kevin
none Details | Review
streamsynchronizer: Send GAP event in event process thread, not data thread (2.88 KB, patch)
2015-02-15 09:11 UTC, kevin
none Details | Review
streamsynchronizer: Unit test for streamsynchronizer handle eos (6.63 KB, patch)
2015-02-15 09:11 UTC, kevin
none Details | Review
streamsynchronizer: Optimize wait when handle SEGMENT event (2.16 KB, patch)
2015-02-15 09:11 UTC, kevin
needs-work Details | Review
streamsynchronizer: Send GAP event to finish preroll when change state from PLAYING to PAUSED (11.45 KB, patch)
2015-02-16 11:24 UTC, kevin
committed Details | Review
streamsynchronizer: Send GAP event in event process thread, not data thread (2.88 KB, patch)
2015-02-16 11:25 UTC, kevin
committed Details | Review
streamsynchronizer: Unit test for streamsynchronizer handle eos (6.63 KB, patch)
2015-02-16 11:25 UTC, kevin
committed Details | Review
streamsynchronizer: Optimize wait when handle SEGMENT event (2.49 KB, patch)
2015-02-16 11:26 UTC, kevin
committed Details | Review
patch to workaround unit test fail issue. (1.34 KB, patch)
2015-02-27 07:09 UTC, kevin
none Details | Review
Remove unnecessary ERROR message (992 bytes, patch)
2015-02-27 08:56 UTC, kevin
committed Details | Review
patch to fix unit test fail. (1.94 KB, patch)
2015-02-27 15:34 UTC, kevin
none Details | Review
Don't need preroll if have prerolled when state change from PLAYING to PAUSED (1.15 KB, patch)
2015-03-03 07:22 UTC, kevin
rejected Details | Review
Should wait state change complete before start another state change. (2.10 KB, patch)
2015-03-03 07:22 UTC, kevin
committed Details | Review
Log for basesink hang issue. (129.31 KB, text/plain)
2015-03-05 00:25 UTC, kevin
  Details
basesink log for sink has been paused when state change from PLAYING to PAUSED. (1.38 MB, text/plain)
2015-03-12 15:01 UTC, kevin
  Details
queue2: Add buffer-full-time, buffer-full-buffers, buffer-full-size properties (6.85 KB, patch)
2017-05-25 17:46 UTC, GstBlub
none Details | Review

Description kevin 2014-09-15 03:14:25 UTC
I have some clips which audio is shorter than video. The player can't change state from PLAYING to PAUSE state after audio is finish and video is playing. The root cause is audio sink hasn't received GAP event and can't finish preroll and video sink changed to PAUSE state.
It is ok if send EOS when audio finish, but streamsynchronizer send GAP event. I haven't seem streamsynchronizer send GAP event when player change state from PLAYING to PAUSE state.
Can give me some advice or do you need more information?
Comment 1 kevin 2014-09-16 07:17:07 UTC
Below is my understand:
o	Change EOS event to GAP event is used to keep sink alive when one track is shorter than other. So can select another track and sink can work rightly.
o	Gststreamsynchronizer will send GAP event when one track reach EOS and another track still has data. And will send all tracks EOS when all track reach EOS.
o	The shorter track sink can’t get buffer for preroll as another track is prerolled and blocked on gst_pad_push() function. So can’t finish state change to PAUSE.
Comment 2 kevin 2014-09-16 08:37:22 UTC
My solution is send GAP when PLAYING to PAUSE in gststreamsynchronizer to let sink finish preroll. I will try it.
Comment 3 Sebastian Dröge (slomo) 2014-09-16 08:47:14 UTC
All elements that don't sync to the clock have to handle PLAYING and PAUSED the same usually, so this seems wrong.

(In reply to comment #1)
> o    The shorter track sink can’t get buffer for preroll as another track is
> prerolled and blocked on gst_pad_push() function. So can’t finish state change
> to PAUSE.

Why is it blocked? Are you missing queues before the video input and audio input of streamsynchronizer and after it? Can you show a backtrace of all threads in that situation?
Comment 4 kevin 2014-09-16 08:58:32 UTC
I used playbin. below is the thread info.
(gdb) info th
  Id   Target Id         Frame 
  13   Thread 0x6c2f3460 (LWP 13074) "aqueue:src" 0x76fa0c98 in __pthread_cond_wait (cond=0x74b487f8, mutex=0x0) at pthread_cond_wait.c:187
  12   Thread 0x6cb42460 (LWP 13073) "vqueue:src" 0x76fa0c98 in __pthread_cond_wait (cond=0x1ba650, mutex=0x0) at pthread_cond_wait.c:187
  11   Thread 0x6d342460 (LWP 13072) "compositor thre" 0x76fa0c98 in __pthread_cond_wait (cond=0x74b42848, mutex=0x0) at pthread_cond_wait.c:
187
  10   Thread 0x720d3460 (LWP 13068) "threaded-ml" 0x76f20c00 in poll ()
    at ../sysdeps/unix/syscall-template.S:81
  8    Thread 0x72afa460 (LWP 13065) "multiqueue0:src" 0x76fa0c98 in __pthread_cond_wait (cond=0x19f1a0, mutex=0x0) at pthread_cond_wait.c:18
7
  7    Thread 0x738cd460 (LWP 13064) "multiqueue0:src" 0x76fa0c98 in __pthread_cond_wait (cond=0x74b453a0, mutex=0x0) at pthread_cond_wait.c:
187
  6    Thread 0x740cd460 (LWP 13063) "aiurdemux0:sink" 0x76fa0c98 in __pthread_cond_wait (cond=0x74200d98, mutex=0x0) at pthread_cond_wait.c:
187
  5    Thread 0x74aff460 (LWP 13062) "typefind:sink" 0x76fa0c98 in __pthread_cond_wait (cond=0x198bc8, mutex=0x0) at pthread_cond_wait.c:187
  4    Thread 0x754ff460 (LWP 13061) "gplay-1.0" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x1c860, mutex=0x0, mutex@entry=0x1c834)
    at pthread_cond_wait.c:187
  3    Thread 0x75eaa460 (LWP 13060) "gplay-1.0" 0x76fa4c10 in nanosleep ()
    at ../sysdeps/unix/syscall-template.S:81
  2    Thread 0x766aa460 (LWP 13059) "gplay-1.0" 0x76efab70 in nanosleep ()
---Type <return> to continue, or q <return> to quit---
    at ../sysdeps/unix/syscall-template.S:81
* 1    Thread 0x76ff8000 (LWP 13055) "gplay-1.0" 0x76fa1050 in __pthread_cond_timedwait (cond=0x162fa0, mutex=0x0, abstime=0x7efff628)
    at pthread_cond_timedwait.c:194
Comment 5 kevin 2014-09-16 09:00:55 UTC
which thread back trace do you need or you need all thread back trace?
Comment 6 Sebastian Dröge (slomo) 2014-09-16 09:20:21 UTC
Of all threads :) "thread apply all bt" in gdb
Comment 7 kevin 2014-09-16 09:35:20 UTC
(gdb) info th
  Id   Target Id         Frame 
  14   Thread 0x63b25460 (LWP 1223) "pool" 0x76fa1050 in __pthread_cond_timedwait (cond=cond@entry=0x26780, mutex=0x0, abstime=abstime@entry=
0x63b24d98)
    at pthread_cond_timedwait.c:194
  13   Thread 0x6c383460 (LWP 1222) "aqueue:src" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x74b499a8, mutex=0x0) at pthread_cond_wa
it.c:187
  12   Thread 0x6cbd2460 (LWP 1221) "vqueue:src" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x11a8a0, mutex=0x0) at pthread_cond_wait
.c:187
  11   Thread 0x6d3d2460 (LWP 1220) "compositor thre" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x74b48800, mutex=0x0) at pthread_co
nd_wait.c:187
  10   Thread 0x72163460 (LWP 1218) "threaded-ml" 0x76f20c00 in poll ()
    at ../sysdeps/unix/syscall-template.S:81
  8    Thread 0x72b95460 (LWP 1216) "multiqueue0:src" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x163988, mutex=0x0) at pthread_cond
_wait.c:187
  7    Thread 0x739b8460 (LWP 1215) "multiqueue0:src" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x74b465c8, mutex=0x0) at pthread_co
nd_wait.c:187
  6    Thread 0x741cf460 (LWP 1214) "aiurdemux0:sink" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x74c00d98, mutex=0x0) at pthread_co
nd_wait.c:187
  5    Thread 0x74aff460 (LWP 1213) "typefind:sink" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x136600, mutex=0x0) at pthread_cond_w
ait.c:187
  4    Thread 0x754ff460 (LWP 1212) "gplay-1.0" 0x76fa0c98 in __pthread_cond_wait (cond=cond@entry=0x1c860, mutex=0x0, mutex@entry=0x1c834)
    at pthread_cond_wait.c:187
---Type <return> to continue, or q <return> to quit---
  3    Thread 0x75eb5460 (LWP 1211) "gplay-1.0" 0x76fa4c10 in nanosleep ()
    at ../sysdeps/unix/syscall-template.S:81
  2    Thread 0x766b5460 (LWP 1210) "gplay-1.0" 0x76efab70 in nanosleep ()
    at ../sysdeps/unix/syscall-template.S:81
* 1    Thread 0x76ff8000 (LWP 1205) "gplay-1.0" 0x76fa1050 in __pthread_cond_timedwait (cond=cond@entry=0x10fc88, mutex=0x0, abstime=abstime@
entry=0x7efff688)
    at pthread_cond_timedwait.c:194
(gdb) t 14
[Switching to thread 14 (Thread 0x63b25460 (LWP 1223))]
  • #0 __pthread_cond_timedwait
  • #0 __pthread_cond_timedwait
    at pthread_cond_timedwait.c line 194
  • #1 g_cond_wait_until
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 870
  • #2 g_async_queue_pop_intern_unlocked
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gasyncqueue.c line 424
  • #3 g_async_queue_timeout_pop
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gasyncqueue.c line 545
  • #4 g_thread_pool_wait_for_new_pool
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 169
  • #5 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 366
  • #6 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #7 start_thread
    at pthread_create.c line 314
  • #8 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #9 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 gst_queue_loop
    at gstqueue.c line 1232
  • #3 gst_task_func
    at gsttask.c line 316
  • #4 default_func
    at gsttaskpool.c line 70
  • #5 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #6 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #7 start_thread
    at pthread_create.c line 314
  • #8 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #9 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 gst_base_sink_wait_preroll
    at gstbasesink.c line 2153
  • #3 gst_base_sink_do_preroll
    at gstbasesink.c line 2243
  • #4 gst_base_sink_do_sync
    at gstbasesink.c line 2445
  • #5 gst_base_sink_chain_unlocked
    at gstbasesink.c line 3341
  • #6 gst_base_sink_chain_main
    at gstbasesink.c line 3486
  • #7 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #8 gst_pad_push_data
    at gstpad.c line 3990
  • #9 gst_queue_push_one
    at gstqueue.c line 1115
  • #10 gst_queue_loop
    at gstqueue.c line 1244
  • #11 gst_task_func
    at gsttask.c line 316
  • #12 default_func
    at gsttaskpool.c line 70
  • #13 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #14 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #15 start_thread
    at pthread_create.c line 314
  • #16 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #17 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 compositor_compositing_thread
    at compositor.c line 282
  • #3 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #4 start_thread
    at pthread_create.c line 314
  • #5 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #6 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 poll
    at ../sysdeps/unix/syscall-template.S line 81
  • #0 poll
    at ../sysdeps/unix/syscall-template.S line 81
  • #1 ??
    from /usr/lib/libpulse.so.0
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 _gst_data_queue_wait_non_empty
    at gstdataqueue.c line 551
  • #3 gst_data_queue_pop
    at gstdataqueue.c line 593
  • #4 gst_multi_queue_loop
    at gstmultiqueue.c line 1218
  • #5 gst_task_func
    at gsttask.c line 316
  • #6 default_func
    at gsttaskpool.c line 70
  • #7 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #8 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #9 start_thread
    at pthread_create.c line 314
  • #10 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #11 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 gst_queue_chain
    at gstqueue.c line 1011
  • #3 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #4 gst_pad_push_data
    at gstpad.c line 3990
  • #5 gst_proxy_pad_chain_default
    at gstghostpad.c line 128
  • #6 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #7 gst_pad_push_data
    at gstpad.c line 3990
  • #8 gst_stream_synchronizer_sink_chain
    at gststreamsynchronizer.c line 576
  • #9 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #10 gst_pad_push_data
    at gstpad.c line 3990
  • #11 gst_proxy_pad_chain_default
    at gstghostpad.c line 128
  • #12 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #13 gst_pad_push_data
    at gstpad.c line 3990
  • #14 gst_selector_pad_chain
    at gstinputselector.c line 1108
  • #15 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #16 gst_pad_push_data
    at gstpad.c line 3990
  • #17 gst_proxy_pad_chain_default
    at gstghostpad.c line 128
  • #18 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #19 gst_pad_push_data
    at gstpad.c line 3990
  • #20 gst_proxy_pad_chain_default
    at gstghostpad.c line 128
  • #21 gst_pad_chain_data_unchecked
    at gstpad.c line 3760
  • #22 gst_pad_push_data
  • #23 gst_video_decoder_clip_and_push_buf
    at gstvideodecoder.c line 2657
  • #24 gst_video_decoder_finish_frame
    at gstvideodecoder.c line 2572
  • #25 gst_vpu_dec_object_send_output
    at gstvpudecobject.c line 925
  • #26 gst_vpu_dec_object_decode
    at gstvpudecobject.c line 1132
  • #27 gst_video_decoder_drain_out
    at gstvideodecoder.c line 959
  • #28 gst_video_decoder_sink_event_default
    at gstvideodecoder.c line 1016
  • #29 gst_video_decoder_sink_event
    at gstvideodecoder.c line 1184
  • #30 gst_pad_send_event_unchecked
    at gstpad.c line 5035
  • #31 gst_pad_push_event_unchecked
  • #32 push_sticky
    at gstpad.c line 3370
  • #33 events_foreach
    at gstpad.c line 530
  • #34 check_sticky
    at gstpad.c line 3426
  • #35 gst_pad_push_event
    at gstpad.c line 4848
  • #36 gst_single_queue_push_one
    at gstmultiqueue.c line 1112
  • #37 gst_multi_queue_loop
    at gstmultiqueue.c line 1338
  • #38 gst_task_func
    at gsttask.c line 316
  • #39 default_func
    at gsttaskpool.c line 70
  • #40 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #41 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #42 start_thread
    at pthread_create.c line 314
  • #43 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #44 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 gst_task_func
    at gsttask.c line 301
  • #3 default_func
    at gsttaskpool.c line 70
  • #4 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #5 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #6 start_thread
    at pthread_create.c line 314
  • #7 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #8 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 g_cond_wait
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 753
  • #2 gst_task_func
    at gsttask.c line 301
  • #3 default_func
    at gsttaskpool.c line 70
  • #4 g_thread_pool_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthreadpool.c line 309
  • #5 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #6 start_thread
    at pthread_create.c line 314
  • #7 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #8 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #0 __pthread_cond_wait
    at pthread_cond_wait.c line 187
  • #1 fsl_player_queue_get
    at fsl_player_queue.c line 319
  • #2 fsl_player_wait_message
    at mfw_gplay_core.c line 2130
  • #3 msg_thread_fun
    at mfw_gplay.c line 568
  • #4 start_thread
    at pthread_create.c line 314
  • #5 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #6 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 nanosleep
    at ../sysdeps/unix/syscall-template.S line 81
  • #0 nanosleep
    at ../sysdeps/unix/syscall-template.S line 81
  • #1 FSL_PLAYER_SLEEP
    at fsl_player_osal.c line 138
  • #2 display_thread_fun
    at mfw_gplay.c line 550
  • #3 start_thread
    at pthread_create.c line 314
  • #4 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #5 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 nanosleep
    at ../sysdeps/unix/syscall-template.S line 81
  • #0 nanosleep
    at ../sysdeps/unix/syscall-template.S line 81
  • #1 usleep
    at ../sysdeps/unix/sysv/linux/usleep.c line 32
  • #2 g_main_loop_thread_fun
    at mfw_gplay_core.c line 270
  • #3 g_thread_proxy
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread.c line 798
  • #4 start_thread
    at pthread_create.c line 314
  • #5 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #6 ??
    at ../ports/sysdeps/unix/sysv/linux/arm/clone.S line 92
  • #0 __pthread_cond_timedwait
  • #0 __pthread_cond_timedwait
    at pthread_cond_timedwait.c line 194
  • #1 g_cond_wait_until
    at /usr/src/debug/glib-2.0/1_2.38.2-r0/glib-2.38.2/glib/gthread-posix.c line 870
  • #2 gst_element_get_state_func
    at gstelement.c line 2053
  • #3 gst_bin_get_state_func
    at gstbin.c line 1829
  • #4 gst_element_get_state
    at gstelement.c line 2163
  • #5 poll_for_state_change
    at mfw_gplay_core.c line 198
  • #6 fsl_player_pause
    at mfw_gplay_core.c line 1165
  • #7 main
    at mfw_gplay.c line 816

Comment 8 Sebastian Dröge (slomo) 2014-09-16 09:45:19 UTC
The problem here seems that the videodecoder goes directly into the streamsynchronizer without a queue between them.
Comment 9 kevin 2014-09-16 09:52:00 UTC
I don't understand. Why queue between video decoder and streamsynchronizer can effect sink preroll?
Comment 10 Sebastian Dröge (slomo) 2014-09-16 09:57:57 UTC
Actually you're right, it shouldn't. And I assume it doesn't make a difference?

Where is the backtrace of the audio related threads? There should be one from the multiqueue through the decoder to streamsynchronizer, and another to the audio sink (and I only see one thread with a sink).

What exactly is the situation at this point? I see that the video is trying to pre-roll but there is nothing from the audio anywhere. Did streamsynchronizer receive EOS for the audio already and that's why nothing is to be seen for the audio? If so it should've sent a GAP event to the audio sink, so the audio sink prerolls and the video sink can finish prerolling.
Comment 11 kevin 2014-09-16 10:12:30 UTC
The test clip is audio shorter than video. user pause the player when audio already EOS and video is playing.

My understand is: 
1. sink will finish pre-roll if received GAP event; 
2. streamsynchronizer should send GAP event when PLAYING to PAUSE state as it is blocked by gst_pad_push() function in chain function and can't send GAP event;
3. video already reach PAUSE state as video sink already received one buffer;

Is my understand right?
Comment 12 Sebastian Dröge (slomo) 2014-09-16 11:43:28 UTC
1) and 3) is correct, 2) is not. streamsynchronizer is not blocked anywhere for the audio pad, but it should've received the EOS event there before. As result of the EOS event it should've sent a GAP event downstream to pre-roll the audio sink.
Comment 13 Tim-Philipp Müller 2014-09-16 12:14:24 UTC
Any chance you could make the clip available that triggers this problem?
Comment 14 kevin 2014-09-16 13:46:48 UTC
(In reply to comment #12)
> 1) and 3) is correct, 2) is not. streamsynchronizer is not blocked anywhere for
> the audio pad, but it should've received the EOS event there before. As result
> of the EOS event it should've sent a GAP event downstream to pre-roll the audio
> sink.

Which element should send the GAP event and how?

(In reply to comment #13)
> Any chance you could make the clip available that triggers this problem?

I found same issue on: https://bugzilla.gnome.org/show_bug.cgi?id=633700
maybe you can get test clip on that thread.
Comment 15 Sebastian Dröge (slomo) 2014-09-16 13:50:53 UTC
(In reply to comment #14)
> (In reply to comment #12)
> > 1) and 3) is correct, 2) is not. streamsynchronizer is not blocked anywhere for
> > the audio pad, but it should've received the EOS event there before. As result
> > of the EOS event it should've sent a GAP event downstream to pre-roll the audio
> > sink.
> 
> Which element should send the GAP event and how?

streamsynchronizer, when it receives EOS on a stream without a buffer before that and when not forwarding EOS (because not all streams are EOS yet).
Comment 16 kevin 2014-09-16 14:04:19 UTC
streamsynchronizer already send the GAP event when received EOS and not forwarding EOS from the code.
Comment 17 Sebastian Dröge (slomo) 2014-09-17 06:47:38 UTC
Then the question would be why the audio sink does not pre-roll because of that GAP event. Can you check what happens there?
Comment 18 kevin 2014-09-17 07:09:33 UTC
I can check it. But below is my concern:

1) streamsynchronizer audio sink pad chain will never be called after audio reach EOS.
2) streamsynchronizer will send GAP to audio sink after received video buffer and pushed to video sink.
3) audio sink need GAP event to pre-roll every time when PLAYING to PAUSE. so streamersynchronizer should send GAP every time when PLAYING to PAUSE.
4) streamersynchronizer video pad chain will be block after video sink received one buffer and changed to PAUSE state. the reason is video sink is PAUSED and queue between streamsynchronizer and video sink is full.

Is it right? Can the GAP event send by streamsynchronizer when audio EOS cause audio sink pre-rolled all the time?
Comment 19 Sebastian Dröge (slomo) 2014-09-17 07:16:43 UTC
I see your concern. Maybe streamsynchronizer really has to send a GAP event whenever going from PLAYING to PAUSED if a stream is at EOS but the others are not yet. Does this fix all the problems for you?
Comment 20 kevin 2014-09-17 07:50:07 UTC
I have tried it, but found audio sink till has issue. I will try to fix it in base sink and send out the patch for your review.
Comment 21 kevin 2014-09-17 09:44:08 UTC
Created attachment 286349 [details] [review]
basesink need call do_preroll when received GAP event.
Comment 22 kevin 2014-09-17 09:45:20 UTC
Created attachment 286350 [details] [review]
streamsynchronizer need send GAP event when state change from PLAYING to PAUSE.
Comment 23 Sebastian Dröge (slomo) 2014-09-18 10:40:10 UTC
Some relevant discussion from IRC:

<slomo_> wtay_: https://bugzilla.gnome.org/show_bug.cgi?id=736655 what do you think of these patches?
<wtay_> slomo_, I think in general we agreed that a GAP event should preroll a sink and we can do that now because caps are not on the buffer so the sink should be negotiated and ready to go
<wtay_> but audiosink should play silence for the GAP event, I doubt it does that now
<slomo_> yes to both
<slomo_> but sending the GAP event in PLAYING->PAUSED in streamsynchronizer? :)
<wtay_> that does not seem right
<slomo_> good, i agree ;) do you have another idea?
<wtay_> I can only think of sending a gap event with unlimited duration, which should make basesink go to play<->paused without ASYNC
<wtay_> theoretically, I doubt it does that currently
<slomo_> and make it switch between those states forever without need for a further GAP event?
<wtay_> I know audiosink goes to paused SYNC when it still has unplayed audio (unplayed as in not yet sent to the device)
Comment 24 kevin 2014-09-19 05:20:30 UTC
So the solution is like below:
1) streamsynchronizer will send GAP event with unlimited duration when received short track EOS.
2 ) basesink will treat the GAP event with unlimited duration specially and needn't pre-roll when change to PAUSE state.
3 ) streamsynchronizer needn't send GAP event when process pad chain function for the short track.

I checked the latest GST 1.x code, 1) is ready, 2) basesink hasn't set needn't pre-roll flag when received GAP event which has unlimited duration. 3) streamsynchronizer still send GAP in pad chain function when one track reach EOS.

If your agree above method, I will generate patch to fix those issue.
Comment 25 Sebastian Dröge (slomo) 2014-09-19 06:53:45 UTC
That sounds correct, yes. 2) also needs to go back into normal pre-rolling mode if receiving a buffer or finite GAP event.
Comment 26 kevin 2014-09-19 08:18:59 UTC
I checked gstaudiobasesink, the GAP event with unlimited duration and EOS have same handle code. So, if we use above method, streamsynchronizer send GAP event with unlimited duration is totally same with EOS event. Do we miss something? What is the reason of change EOS event to GAP event?
Comment 27 kevin 2014-10-10 02:07:28 UTC
Do your gays has any idea? I have environment to reproduce the issue and try any idea.
Comment 28 Sebastian Dröge (slomo) 2014-12-24 12:51:33 UTC
What's the status here now and the summary of the situation? Also isn't this the same or closely related to bug #727074?
Comment 29 kevin 2014-12-25 04:54:37 UTC
Will check it on Gstreamer 1.4.1 later and feedback.
Comment 30 kevin 2014-12-25 04:57:08 UTC
*** Bug 727074 has been marked as a duplicate of this bug. ***
Comment 31 kevin 2014-12-25 04:59:08 UTC
*** Bug 726854 has been marked as a duplicate of this bug. ***
Comment 32 kevin 2014-12-30 02:23:27 UTC
I checked the code, propose below method to fix the issue. Please help to review:

1. Audio sink will generate unlimited length mute audio when received GAP event with -1 duration and write audio device until flush, state change. gst_base_sink_wait_preroll() will call when interrupt and needn't preroll buffer to finish preroll.
2. Video sink will enter GAP_EOS states after received GAP event with -1 duration and will call gst_base_sink_wait_preroll() when flush, state change, so needn't buffer to finish preroll.
3. Streamersynchronizor needn't send GAP event in chain() function. Only need send GAP event when received EOS event.

Is it reasonable? Will try the method and upload patch if the method is ok.
Comment 33 Sebastian Dröge (slomo) 2014-12-30 09:07:48 UTC
No, that won't work unfortunately. A GAP event with duration=-1 is supposed to indicate a GAP of unknown length that will end at the timestamp of the next buffer or GAP event.
Comment 34 kevin 2014-12-30 09:44:55 UTC
My proposal also need end if buffer with timestamp or GAP event come.
I think streamsynchronizor send GAP in chain() function has much limitation. We also has use case to send audio EOS when rewind. Can it support rewind?

If the proposals all reject, can you give some suggestion?
Comment 35 Sebastian Dröge (slomo) 2014-12-30 10:01:23 UTC
I don't see the relation between EOS and rewind. What exactly are you doing there?

In your proposal you said that the audio sink will generate unlimited length silence audio if a GAP event with duration=-1 is received. However it should only generate silence until the timestamp of the next buffer or GAP event.

Same goes for video, it should not change any state but continue showing the previous frame (or black if there was none) until the timestamp of the next buffer instead of forever.
Comment 36 kevin 2014-12-30 10:06:17 UTC
If one file has big gap in the middle of file, you can get the next valid audio packet. So you don't know the next valid audio time stamp.
Comment 37 kevin 2014-12-30 10:10:11 UTC
As I checked before, streamsynchroinzor can't send GAP event in chain() function when rewind after audio track send EOS and video is rewinding.
Comment 38 kevin 2014-12-30 10:12:40 UTC
For video sink change, the purpose is finish pre-roll when change to PAUSE state. Not actural change state.
Comment 39 Sebastian Dröge (slomo) 2014-12-30 10:27:04 UTC
(In reply to comment #36)
> If one file has big gap in the middle of file, you can get the next valid audio
> packet. So you don't know the next valid audio time stamp.

Yes, you only know it once it is there. Which is why I'm not sure yet what the correct solution here would be. If you just generate infinite amount of silence, you will never play anything after such a GAP event.

(In reply to comment #37)
> As I checked before, streamsynchroinzor can't send GAP event in chain()
> function when rewind after audio track send EOS and video is rewinding.

What do you mean with rewind here?
Comment 40 kevin 2014-12-30 12:12:20 UTC
The infinite amount of silence should be interrupt by buffer or GAP event. Some thing like 20 ms one time. If still no buffer or GAP event, playback another 20 ms.

Rewind case is our demux will send EOS to audio track when rewind. Video will only send key frame. I remember streamsynchronizer can't send GAP event to audio sink. Should be a bug.

The root cause of the issue in the thread is audio sink can't finish pre-roll as GAP event can't be send as streamsynchronizer blocked on video push. And the GAP event send in chain function has many conditions (so it will can't be send). So is there more good resolution to handle this?
Comment 41 Sebastian Dröge (slomo) 2014-12-30 12:21:49 UTC
(In reply to comment #40)
> The infinite amount of silence should be interrupt by buffer or GAP event. Some
> thing like 20 ms one time. If still no buffer or GAP event, playback another 20
> ms.

That's what would automatically happen in GstAudioBaseSink if the ringbuffer is started. It would output silence all the time unless some real data is written into the ringbuffer.
So there shouldn't be much necessary for this change, other than starting the ringbuffer and prerolling on GAP events (and EOS too, I remember seeing a bug about that in Bugzilla recently).

> Rewind case is our demux will send EOS to audio track when rewind. Video will
> only send key frame. I remember streamsynchronizer can't send GAP event to
> audio sink. Should be a bug.
> 
> The root cause of the issue in the thread is audio sink can't finish pre-roll
> as GAP event can't be send as streamsynchronizer blocked on video push. And the
> GAP event send in chain function has many conditions (so it will can't be
> send). So is there more good resolution to handle this?

I don't understand what you mean here.
Comment 42 kevin 2014-12-30 14:21:17 UTC
If audio is shorter and reach EOS, audio GAP event will send after video pad pushed video buffer in streamsynchronizer. Video pipeline should be full with data in normal playback. Video pad push will blocked when change to PAUSE state and audio GAP event can't be send out. Audio sink can't finish pre-roll. The attachment patch will send GAP event during PLAYING to PAUSE state change. But the patch reject. Can put send GAP event before video pad push?

If one stream seek to only video part, audio will send Segment and EOS. But as audio decoder no output, so Segment can't send out of audio decoder. Below code in streamsynchronizer will bypass send GAP. How to handle this?

      if (!ostream->is_eos || ostream->segment.format != GST_FORMAT_TIME)
        continue;
Comment 43 kevin 2014-12-31 03:36:08 UTC
I have verified the issue on Gstreamer 1.4.1 and read whole thread to refresh my head.

Still has the issue on Gstreamer 1.4.1. But the second attached patch can fix the issue, don't need the first patch any more. But the method has been reject base on comment #23. The root cause of the issue is video pad push buffer blocked when state change from PLAYING to PAUSE and can't send out audio GAP event in streamsynchronizer to finish audio sink pre-roll.

Based on comment #24 and #25. You agree below method.

1) streamsynchronizer will send GAP event with unlimited duration when received
short track EOS.
2 ) basesink will treat the GAP event with unlimited duration specially and
needn't pre-roll when change to PAUSE state. basesink will change to normal pre-roll after received buffer or GAP with duration or flush.
3 ) streamsynchronizer needn't send GAP event when process pad chain function
for the short track.

Do you still think the method is ok? if so, I will try it and upload to review.
Comment 44 Sebastian Dröge (slomo) 2015-01-08 13:10:24 UTC
1) yes
2) GAP events should *always* preroll a sink
3) what do you mean with that?
Comment 45 kevin 2015-01-09 10:17:09 UTC
The key point is audiosink can't received GAP to finish pre-roll. The root cause is video pad push has been blocked. Don't know how to fix it. Can you advice?
Comment 46 Sebastian Dröge (slomo) 2015-01-09 10:55:26 UTC
Why can't the audiosink receive the GAP event because something in the video part of the pipeline is blocked?
Comment 47 kevin 2015-01-09 14:34:18 UTC
As talked on IM. EOS event will blocked on event function and will send GAP event after post one GCond and than block again. Should unblock after flush or shutdown. Should post GCond when change state from PLAYING to PAUSE.

Do need post when PAUSE to PLAYING? sink hasn't any dependence when change state from PAUSE to PLAYING.
Comment 48 Sebastian Dröge (slomo) 2015-01-09 14:46:21 UTC
Summary of IRC dicussion is to change streamsynchronizer to:

a) Wait in the event function when EOS is received until all are EOS on a GCond, and forward EOS from there then (currently we send EOS from the event function of the last EOS pad)
b) Change the GAP events that are currently sent from the chain function of a pad to all other pads that are late. They should be sent from the event function of that specific pad instead by waking up the GCond shortly.
c) Send a new GAP event from the event function whenever going from PAUSED->PLAYING and PLAYING->PAUSED by shortly waking up the GCond.
d) Wake up the GCond and forward the EOS event when either the last pad went EOS, or a flush-start event is received on that pad (or its corresponding srcpad), or the streamsynchronizer goes from PAUSED->READY.


a) and b) also fix potential deadlocks.
Comment 49 Sebastian Dröge (slomo) 2015-01-09 14:47:09 UTC
This bug is also related (or the same?) as bug #633700.
Comment 50 kevin 2015-01-09 16:09:56 UTC
For b), do need add position and duration into the GAP event? How to get it? How to get it if no segment? Should it same as current code in chain function? Lag one second?
Comment 51 Sebastian Dröge (slomo) 2015-01-09 16:11:52 UTC
Yes, the same values as are used right now. You need to pass them over to the other thread so it can be used after waking up from the GCond.
Comment 52 kevin 2015-01-11 04:44:49 UTC
I mentioned streamsynchronizer hasn't received segment when EOS is fixed by below commit. So should be not the concern.
But I can't see same process in video decoder base. Do we need add send pending event when received EOS in video decoder base?

commit b2413d46edb7c97d1ad4e2535dd5fe737b0c04bc
Author: Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
Date:   Fri Nov 28 15:06:27 2014 +0100

    audiodecoder: Push pending events before sending EOS.

    Segments are added to the pending events, and pushing a segment
    is mandatory before sending EOS.

    + Adds a test.

    https://bugzilla.gnome.org/show_bug.cgi?id=740853
Comment 53 kevin 2015-01-11 13:19:37 UTC
Element should send pending sticky events before send EOS event? Is it right? 

We have clips which has valid media information for audio track, but haven't any audio data, we can't get audio TAG information on playbin. Audio and video decoder base should send pending sticky events before send EOS event. This is another issue, should submit one ticket for it. But segment event must send before EOS, or streamsynchrinozer can't work.
Comment 54 Tim-Philipp Müller 2015-01-11 20:04:36 UTC
> Element should send pending sticky events before send EOS event? Is it right? 

I think so, yes. Please file a separate bug for that.
Comment 55 kevin 2015-01-12 02:06:07 UTC
filed one bug with below link:
https://bugzilla.gnome.org/show_bug.cgi?id=742789
Comment 56 kevin 2015-01-12 08:23:58 UTC
Created attachment 294326 [details] [review]
patch to fix the issue.
Comment 57 kevin 2015-01-12 08:36:42 UTC
The patch haven't include below statement:

b) Change the GAP events that are currently sent from the chain function of a
pad to all other pads that are late. They should be sent from the event
function of that specific pad instead by waking up the GCond shortly.

Will do it later.


Note:

Will send eos immediately if not seen any data. As decoder can't get output caps if can't decode any valid audio data. Sink will report error (not negotiate) if send GAP event.
Comment 58 kevin 2015-01-12 09:52:03 UTC
Created attachment 294330 [details] [review]
Patch for item b)
Comment 59 kevin 2015-01-13 04:26:07 UTC
Created attachment 294382 [details] [review]
update the patch for minor change.

seen_data changed to mean received data in the whole time. not received data after seek.
Comment 60 kevin 2015-01-13 04:26:54 UTC
Created attachment 294383 [details] [review]
update the patch
Comment 61 kevin 2015-01-14 10:09:10 UTC
Will write unit test for the case and upload patch later.
Comment 62 Sebastian Dröge (slomo) 2015-01-19 11:46:59 UTC
Review of attachment 294382 [details] [review]:

I think in general you need to call gst_pad_get_element_private() again whenever you have unlocked the mutex and locked it again. in the meantime the application could've removed the pad from streamsynchronizer, and freed the stream.

::: gst/playback/gststreamsynchronizer.c
@@ +493,3 @@
+      if (!seen_data) {
+        /* no buffer form the pad anytime, not negotiate, send eos */
+        stream->eos_sent = TRUE;

Should probably also only be set when it happens below

@@ +504,3 @@
         timestamp = stream->segment.stop;
 
+      stream->segment.position = timestamp;

Why?

@@ +520,3 @@
           GstStream *ostream = l->data;
           /* local snapshot of current pads */
+          ostream->eos_sent = TRUE;

Should probably be done when it actually happened, not before

@@ +556,3 @@
+          if (self->eos) {
+            GST_DEBUG_OBJECT (pad, "send EOS event");
+            ret = gst_pad_push_event (srcpad, gst_event_new_eos ());

stream->eos_sent = TRUE now?

@@ +561,3 @@
+          /* send eos as sink can't handle if haven't negotiate */
+          GST_DEBUG_OBJECT (pad, "send EOS event");
+          ret = gst_pad_push_event (srcpad, gst_event_new_eos ());

The problem here is that we can't preroll the sink again later here. So after you forwarded EOS, then went to PAUSED and the PLAYING again... nothing will ever be sent to that sink again. Not even an EOS. Maybe another EOS should be sent?
Comment 63 Sebastian Dröge (slomo) 2015-01-19 11:47:41 UTC
Review of attachment 294383 [details] [review]:

::: gst/playback/gststreamsynchronizer.c
@@ +229,3 @@
 
+      event =
+          gst_event_new_gap (stream->segment.position, stream->gap_duration);

Access to GAP duration should probably be protected by the mutex too
Comment 64 kevin 2015-01-20 02:54:01 UTC
(In reply to comment #62)
> Review of attachment 294382 [details] [review]:
> 
> I think in general you need to call gst_pad_get_element_private() again
> whenever you have unlocked the mutex and locked it again. in the meantime the
> application could've removed the pad from streamsynchronizer, and freed the
> stream.
> 
Will fix it in function gst_stream_synchronizer_wait(). Call gst_pad_get_element_private() in while loop.
> ::: gst/playback/gststreamsynchronizer.c
> @@ +493,3 @@
> +      if (!seen_data) {
> +        /* no buffer form the pad anytime, not negotiate, send eos */
> +        stream->eos_sent = TRUE;
> 
> Should probably also only be set when it happens below
> 
Will set it after pushed EOS event.
> @@ +504,3 @@
>          timestamp = stream->segment.stop;
> 
> +      stream->segment.position = timestamp;
> 
> Why?
> 
Record current position and use the position when push GAP event. stream->segment.position also been updated in chain().
> @@ +520,3 @@
>            GstStream *ostream = l->data;
>            /* local snapshot of current pads */
> +          ostream->eos_sent = TRUE;
> 
> Should probably be done when it actually happened, not before
> 
will set it after pushed EOS event.
> @@ +556,3 @@
> +          if (self->eos) {
> +            GST_DEBUG_OBJECT (pad, "send EOS event");
> +            ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> 
> stream->eos_sent = TRUE now?
> 
Yes, get lock and set it as the lock has been released.
> @@ +561,3 @@
> +          /* send eos as sink can't handle if haven't negotiate */
> +          GST_DEBUG_OBJECT (pad, "send EOS event");
> +          ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> 
> The problem here is that we can't preroll the sink again later here. So after
> you forwarded EOS, then went to PAUSED and the PLAYING again... nothing will
> ever be sent to that sink again. Not even an EOS. Maybe another EOS should be
> sent?
This only will be happened when not received data in anytime. So the track is all corrupted. sink don't know how to initialize the sink device.
Sink needn't preroll after received EOS. and streamsynchronizer can't send anything through pad after send EOS event. So pipeline can PAUSE again.
Currently, we use one workaround for the issue. The workaround is send EOS immediately after received EOS. And not found issue with the workaround.
Demux will send EOS again after seek. So seek is ok.
Comment 65 kevin 2015-01-20 02:59:49 UTC
(In reply to comment #63)
> Review of attachment 294383 [details] [review]:
> 
> ::: gst/playback/gststreamsynchronizer.c
> @@ +229,3 @@
> 
> +      event =
> +          gst_event_new_gap (stream->segment.position, stream->gap_duration);
> 
> Access to GAP duration should probably be protected by the mutex too

Those code is in gst_stream_synchronizer_wait(). gst_stream_synchronizer_wait() is called in below code. gst_stream_synchronizer_wait() should be called with GST_STREAM_SYNCHRONIZER_LOCK.

+          GST_STREAM_SYNCHRONIZER_LOCK (self);
+          self->send_gap_event = TRUE;
+          ret = gst_stream_synchronizer_wait (self, srcpad);
+          GST_STREAM_SYNCHRONIZER_UNLOCK (self);
Comment 66 kevin 2015-01-20 05:10:27 UTC
(In reply to comment #62)
> Review of attachment 294382 [details] [review] [details]:
> 
> I think in general you need to call gst_pad_get_element_private() again
> whenever you have unlocked the mutex and locked it again. in the meantime the
> application could've removed the pad from streamsynchronizer, and freed the
> stream.
> 
Will fix it in function gst_stream_synchronizer_wait(). Call
gst_pad_get_element_private() in while loop.
> ::: gst/playback/gststreamsynchronizer.c
> @@ +493,3 @@
> +      if (!seen_data) {
> +        /* no buffer form the pad anytime, not negotiate, send eos */
> +        stream->eos_sent = TRUE;
> 
> Should probably also only be set when it happens below
> 
Will set it after pushed EOS event.
> @@ +504,3 @@
>          timestamp = stream->segment.stop;
> 
> +      stream->segment.position = timestamp;
> 
> Why?
> 
Record current position and use the position when push GAP event.
stream->segment.position also been updated in chain().
> @@ +520,3 @@
>            GstStream *ostream = l->data;
>            /* local snapshot of current pads */
> +          ostream->eos_sent = TRUE;
> 
> Should probably be done when it actually happened, not before
> 
will set it after pushed EOS event.
> @@ +556,3 @@
> +          if (self->eos) {
> +            GST_DEBUG_OBJECT (pad, "send EOS event");
> +            ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> 
> stream->eos_sent = TRUE now?
> 
Yes, get lock and set it as the lock has been released.
> @@ +561,3 @@
> +          /* send eos as sink can't handle if haven't negotiate */
> +          GST_DEBUG_OBJECT (pad, "send EOS event");
> +          ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> 
> The problem here is that we can't preroll the sink again later here. So after
> you forwarded EOS, then went to PAUSED and the PLAYING again... nothing will
> ever be sent to that sink again. Not even an EOS. Maybe another EOS should be
> sent?
This only will be happened when haven't received data in anytime. So the track is all corrupted. sink don't know how to initialize the sink device.
Sink needn't preroll after received EOS. So pipeline can PAUSE again.
Demux will send EOS again after seek. So seek is ok.
Comment 67 kevin 2015-01-20 06:08:21 UTC
Created attachment 294947 [details] [review]
Updated patch based on review.
Comment 68 kevin 2015-01-20 06:09:45 UTC
Created attachment 294948 [details] [review]
Updated patch based on review.
Comment 69 Sebastian Dröge (slomo) 2015-01-20 14:44:05 UTC
(In reply to comment #64)
>
> > +      stream->segment.position = timestamp;
> > 
> > Why?
> > 
> Record current position and use the position when push GAP event.
> stream->segment.position also been updated in chain().

This probably also has to be protected with a mutex then

> > @@ +561,3 @@
> > +          /* send eos as sink can't handle if haven't negotiate */
> > +          GST_DEBUG_OBJECT (pad, "send EOS event");
> > +          ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> > 
> > The problem here is that we can't preroll the sink again later here. So after
> > you forwarded EOS, then went to PAUSED and the PLAYING again... nothing will
> > ever be sent to that sink again. Not even an EOS. Maybe another EOS should be
> > sent?
> This only will be happened when not received data in anytime. So the track is
> all corrupted. sink don't know how to initialize the sink device.
> Sink needn't preroll after received EOS. and streamsynchronizer can't send
> anything through pad after send EOS event. So pipeline can PAUSE again.
> Currently, we use one workaround for the issue. The workaround is send EOS
> immediately after received EOS. And not found issue with the workaround.
> Demux will send EOS again after seek. So seek is ok.

Not sure I can follow :) You mean you have a custom workaround somewhere for the case of PLAYING->PAUSED->PLAYING if you get into this situation? But with just your patch it would never go to PLAYING again?
Comment 70 Sebastian Dröge (slomo) 2015-01-20 14:44:57 UTC
(In reply to comment #65)

> > Access to GAP duration should probably be protected by the mutex too
> 
> Those code is in gst_stream_synchronizer_wait(). gst_stream_synchronizer_wait()
> is called in below code. gst_stream_synchronizer_wait() should be called with
> GST_STREAM_SYNCHRONIZER_LOCK.
> 
> +          GST_STREAM_SYNCHRONIZER_LOCK (self);
> +          self->send_gap_event = TRUE;
> +          ret = gst_stream_synchronizer_wait (self, srcpad);
> +          GST_STREAM_SYNCHRONIZER_UNLOCK (self);

But that means that you send the GAP event while holding that mutex. That's never a good idea :) Don't send any events, queries or buffers downstream while holding the mutex.
Comment 71 Sebastian Dröge (slomo) 2015-01-20 14:45:52 UTC
(In reply to comment #69)
> (In reply to comment #64)
> >
> > > +      stream->segment.position = timestamp;
> > > 
> > > Why?
> > > 
> > Record current position and use the position when push GAP event.
> > stream->segment.position also been updated in chain().
> 
> This probably also has to be protected with a mutex then
> 
> > > @@ +561,3 @@
> > > +          /* send eos as sink can't handle if haven't negotiate */
> > > +          GST_DEBUG_OBJECT (pad, "send EOS event");
> > > +          ret = gst_pad_push_event (srcpad, gst_event_new_eos ());
> > > 
> > > The problem here is that we can't preroll the sink again later here. So after
> > > you forwarded EOS, then went to PAUSED and the PLAYING again... nothing will
> > > ever be sent to that sink again. Not even an EOS. Maybe another EOS should be
> > > sent?
> > This only will be happened when not received data in anytime. So the track is
> > all corrupted. sink don't know how to initialize the sink device.
> > Sink needn't preroll after received EOS. and streamsynchronizer can't send
> > anything through pad after send EOS event. So pipeline can PAUSE again.
> > Currently, we use one workaround for the issue. The workaround is send EOS
> > immediately after received EOS. And not found issue with the workaround.
> > Demux will send EOS again after seek. So seek is ok.
> 
> Not sure I can follow :) You mean you have a custom workaround somewhere for
> the case of PLAYING->PAUSED->PLAYING if you get into this situation? But with
> just your patch it would never go to PLAYING again?

And that it will only go to PLAYING again after a seek, but not when pausing without a seek?
Comment 72 kevin 2015-01-20 16:08:12 UTC
Summary of IRC talk:

Should unlock before send GAP event and hold the lock again after send GAP event. Should call gst_pad_get_element_private() after take the lock after send GAP and EOS event.
Comment 73 kevin 2015-01-20 16:17:34 UTC
Unit test:
1. Source pad should received GAP event after one track reach EOS.
2. Source pad should received GAP event when state change from PLAYING to PAUSE after one track reach EOS.
3. Source pad should received EOS event when one track haven't any data and reach EOS.
Comment 74 kevin 2015-01-21 06:26:47 UTC
Created attachment 295067 [details] [review]
Update based on IRC talk.
Comment 75 kevin 2015-01-21 06:27:30 UTC
Created attachment 295068 [details] [review]
Update based on IRC talk.
Comment 76 kevin 2015-01-21 07:31:01 UTC
Streamsynchronizer Unit Test:
1. Streamsynchronizer sink pad should send GAP event after one track reach EOS when normal playing.
2. Streamsynchronizer sink pad should send GAP event when state change from PLAYING to PAUSE after one track reach EOS.
3. Streamsynchronizer sink pad should send EOS event when one track haven't any data and reach EOS.

Is it ok?
Comment 77 Sebastian Dröge (slomo) 2015-01-21 08:28:51 UTC
Also check for PLAYING->PAUSED->PLAYING, otherwise seems ok. Maybe also don't check for GAP or EOS events, but if a pipeline with a sink after streamsynchronizer works properly in these scenarios (i.e. it prerolls instead of waiting forever). That would seem more future-proof, and check for what we wanted to fix here instead of checking for the solution.
Comment 78 kevin 2015-01-21 09:35:29 UTC
So streamsynchronizer unit test should be below:

A pipeline with a sink after streamsynchronizer can change state from PLAYING->PAUSED->PLAYING in below scenarios:

1. One track reach EOS after pushed some buffers while another track still push buffer.
2. One track reach EOS without buffer while another track still push buffer.

After the pipeline test, do need below test?

1. Streamsynchronizer sink pad should send GAP event after one track reach EOS
when normal playing.
2. Streamsynchronizer sink pad should send GAP event when state change from
PLAYING to PAUSE after one track reach EOS.
3. Streamsynchronizer sink pad should send EOS event when one track haven't any
data and reach EOS.
Comment 79 Sebastian Dröge (slomo) 2015-01-21 09:44:59 UTC
(In reply to comment #78)
> So streamsynchronizer unit test should be below:
> 
> A pipeline with a sink after streamsynchronizer can change state from
> PLAYING->PAUSED->PLAYING in below scenarios:
> 
> 1. One track reach EOS after pushed some buffers while another track still push
> buffer.
> 2. One track reach EOS without buffer while another track still push buffer.

Yes, and not only test if PLAYING->PAUSED->PLAYING works but also if it prerolls when one track goes EOS immediately, while the other one produces data

> After the pipeline test, do need below test?
> [...]

No, those are not needed IMHO. They are testing an implementation detail, not the actual behaviour we're interested in.
Comment 80 kevin 2015-01-23 17:05:44 UTC
Created attachment 295290 [details] [review]
Update based on IRC talk.
Comment 81 kevin 2015-01-23 17:08:43 UTC
Created attachment 295291 [details] [review]
Unit test for streamsynchronizer handle eos.
Comment 82 kevin 2015-01-23 17:10:13 UTC
Upload source code for review firstly.
Comment 83 kevin 2015-01-24 15:03:12 UTC
Created attachment 295329 [details] [review]
Update patch for patch format
Comment 84 kevin 2015-01-24 15:03:56 UTC
Created attachment 295330 [details] [review]
Update patch for patch format
Comment 85 kevin 2015-01-26 03:26:43 UTC
Created attachment 295409 [details] [review]
Send GAP event in event thread.
Comment 86 kevin 2015-01-26 03:27:35 UTC
Created attachment 295410 [details] [review]
Don't send GAP event in chain function.
Comment 87 kevin 2015-01-26 03:28:11 UTC
Created attachment 295411 [details] [review]
Unit test for streamsynchronizer handle eos.
Comment 88 kevin 2015-01-26 03:29:52 UTC
Ready for all patches. Please help to review. Thanks.
Comment 89 Sebastian Dröge (slomo) 2015-01-27 12:21:15 UTC
Thanks, I'll try to go through all this later today :)
Comment 90 kevin 2015-01-28 01:38:30 UTC
Thanks, let me know if need improve the patch.
Comment 91 Sebastian Dröge (slomo) 2015-02-04 10:47:19 UTC
Sorry for still not getting to this, I didn't forget about it :)
Comment 92 Jan Schmidt 2015-02-05 10:02:11 UTC
My latest patches for audiobasesink in Bug 735666 help with the prerolling here, but only up to a point - audiobasesink will process a GAP with timestamp & duration as if it were an audio buffer of the same time+duration and successfully go PLAYING->PAUSED->PLAYING.

That means that streamsynchroniser needs to make sure to send a GAP (or GAPs) that fill the entire time range until it is ready to either start a new stream or send EOS.

It looks like these patches will achieve that, but sending a new GAP when changing state in stream synchroniser, but wouldn't it be better to wake the streams that are looping on EOS every time data passes on another pad that 'moves time forward' and have them send another GAP event then?

With the approach in these patches, new GAP events are only sent when streamsynchroniser changes state, on the presumption that that's also when the audio sinks change state. Maybe there's pipelines where that assumption isn't true?
Comment 93 kevin 2015-02-05 14:18:00 UTC
(In reply to comment #92)
> My latest patches for audiobasesink in Bug 735666 help with the prerolling
> here, but only up to a point - audiobasesink will process a GAP with timestamp
> & duration as if it were an audio buffer of the same time+duration and
> successfully go PLAYING->PAUSED->PLAYING.
> 
How about video sink? Do video sink need GAP event to finish pre-roll when PLAYING->PAUSED?
> That means that streamsynchroniser needs to make sure to send a GAP (or GAPs)
> that fill the entire time range until it is ready to either start a new stream
> or send EOS.
> 
streamsynchronizer will seng GAP with duration is GST_CLOCK_TIME_NONE. Do streamsychronizer need search all segment to get to biggest time range? Or audio sink can handle GST_CLOCK_TIME_NONE.
> It looks like these patches will achieve that, but sending a new GAP when
> changing state in stream synchroniser, but wouldn't it be better to wake the
> streams that are looping on EOS every time data passes on another pad that
> 'moves time forward' and have them send another GAP event then?
> 
streamsynchronizer will send GAP event in chain function. But pad push will be blocked when sink changed to PAUSED state and then can't send GAP event. So need send GAP event when state change in streamsynchronizer.
> With the approach in these patches, new GAP events are only sent when
> streamsynchroniser changes state, on the presumption that that's also when the
> audio sinks change state. Maybe there's pipelines where that assumption isn't
> true?
Comment 94 Jan Schmidt 2015-02-05 14:25:29 UTC
(In reply to comment #93)
> (In reply to comment #92)
> > My latest patches for audiobasesink in Bug 735666 help with the prerolling
> > here, but only up to a point - audiobasesink will process a GAP with timestamp
> > & duration as if it were an audio buffer of the same time+duration and
> > successfully go PLAYING->PAUSED->PLAYING.
> > 
> How about video sink? Do video sink need GAP event to finish pre-roll when
> PLAYING->PAUSED?

The same thing applies to video sinks - they need a GAP to preroll too.

> > That means that streamsynchroniser needs to make sure to send a GAP (or GAPs)
> > that fill the entire time range until it is ready to either start a new stream
> > or send EOS.
> > 
> streamsynchronizer will seng GAP with duration is GST_CLOCK_TIME_NONE. Do
> streamsychronizer need search all segment to get to biggest time range? Or
> audio sink can handle GST_CLOCK_TIME_NONE.

Duration GST_CLOCK_TIME_NONE is handled as duration = 0 in the sink, so it will
effectively 'fill time' up to the start timestamp.

> > It looks like these patches will achieve that, but sending a new GAP when
> > changing state in stream synchroniser, but wouldn't it be better to wake the
> > streams that are looping on EOS every time data passes on another pad that
> > 'moves time forward' and have them send another GAP event then?
> > 
> streamsynchronizer will send GAP event in chain function. But pad push will be
> blocked when sink changed to PAUSED state and then can't send GAP event. So
> need send GAP event when state change in streamsynchronizer.

If pad_push() is blocked, you can't send a GAP event downstream there anyway - the STREAM_LOCK() will be held by the pad_push(). That case should work. The one that is broken is where you sent a GAP downstream from teh EOS event handler, advancing time to some time 'timestamp', and then time advanced past that point, and your pad_push_event (pad, gap_event) returned - you need to either push a new gap, or push a buffer then.
Comment 95 kevin 2015-02-05 14:58:11 UTC
(In reply to comment #94)
> (In reply to comment #93)
> > (In reply to comment #92)
> > > My latest patches for audiobasesink in Bug 735666 help with the prerolling
> > > here, but only up to a point - audiobasesink will process a GAP with timestamp
> > > & duration as if it were an audio buffer of the same time+duration and
> > > successfully go PLAYING->PAUSED->PLAYING.
> > > 
> > How about video sink? Do video sink need GAP event to finish pre-roll when
> > PLAYING->PAUSED?
> 
> The same thing applies to video sinks - they need a GAP to preroll too.
> 
If video is shorter than audio, streamsynchronizer will send GAP event when received video EOS. How to set the GAP duration?
If pipeline change state from PLAYING to PAUSED, do streamsynchronizer need send GAP event again to finish video pre-roll? do streamsynchronizer need send GAP event every time when pipeline change state from PLAYING to PAUSED?
> > > That means that streamsynchroniser needs to make sure to send a GAP (or GAPs)
> > > that fill the entire time range until it is ready to either start a new stream
> > > or send EOS.
> > > 
> > streamsynchronizer will seng GAP with duration is GST_CLOCK_TIME_NONE. Do
> > streamsychronizer need search all segment to get to biggest time range? Or
> > audio sink can handle GST_CLOCK_TIME_NONE.
> 
> Duration GST_CLOCK_TIME_NONE is handled as duration = 0 in the sink, so it will
> effectively 'fill time' up to the start timestamp.
> 
So streamsynchronizer should send GAP with duration is GST_CLOCK_TIME_NONE when received EOS for shorter track? and send GAP event for the shorter track to 'moves time forward' in other track chain function? And send all tracks EOS event after received all tracks EOS event.
> > > It looks like these patches will achieve that, but sending a new GAP when
> > > changing state in stream synchroniser, but wouldn't it be better to wake the
> > > streams that are looping on EOS every time data passes on another pad that
> > > 'moves time forward' and have them send another GAP event then?
> > > 
> > streamsynchronizer will send GAP event in chain function. But pad push will be
> > blocked when sink changed to PAUSED state and then can't send GAP event. So
> > need send GAP event when state change in streamsynchronizer.
> 
> If pad_push() is blocked, you can't send a GAP event downstream there anyway -
> the STREAM_LOCK() will be held by the pad_push(). That case should work. The
> one that is broken is where you sent a GAP downstream from teh EOS event
> handler, advancing time to some time 'timestamp', and then time advanced past
> that point, and your pad_push_event (pad, gap_event) returned - you need to
> either push a new gap, or push a buffer then.

If pad_push() is blocked, streamsynchronizer can't send GAP event for shorter track in chain function. The shorter track is reach EOS, and no any buffer reach streamsynchronizer, so basesink can't finished pre-roll for the shorter track. The longer track is pro-rolled and blocked. So streamsynchronizer need send GAP event for shorter track when state change, and send the GAP event in event handle thread.
Comment 96 Jan Schmidt 2015-02-05 15:13:17 UTC
(In reply to comment #95)
> (In reply to comment #94)
> > (In reply to comment #93)
> > > (In reply to comment #92)
> > > > My latest patches for audiobasesink in Bug 735666 help with the prerolling
> > > > here, but only up to a point - audiobasesink will process a GAP with timestamp
> > > > & duration as if it were an audio buffer of the same time+duration and
> > > > successfully go PLAYING->PAUSED->PLAYING.
> > > > 
> > > How about video sink? Do video sink need GAP event to finish pre-roll when
> > > PLAYING->PAUSED?
> > 
> > The same thing applies to video sinks - they need a GAP to preroll too.
> > 
> If video is shorter than audio, streamsynchronizer will send GAP event when
> received video EOS. How to set the GAP duration?

The duration can be 0 or GST_CLOCK_TIME_NONE, it's fine - the key is that a pad that reaches EOS early should loop in EOS sending gap events downstream with advancing timestamps taken from the other non-EOS stream(s), to keep downstream updated on progress until everything reaches EOS.

The gap events it sends should be based on the maximum position of the other stream(s) - buffer times + duration. The existing logic that calculates a start time for the gap event it sends now is probably fine.

> If pipeline change state from PLAYING to PAUSED, do streamsynchronizer need
> send GAP event again to finish video pre-roll? do streamsynchronizer need send
> GAP event every time when pipeline change state from PLAYING to PAUSED?
> > > > That means that streamsynchroniser needs to make sure to send a GAP (or GAPs)
> > > > that fill the entire time range until it is ready to either start a new stream
> > > > or send EOS.
> > > > 
> > > streamsynchronizer will seng GAP with duration is GST_CLOCK_TIME_NONE. Do
> > > streamsychronizer need search all segment to get to biggest time range? Or
> > > audio sink can handle GST_CLOCK_TIME_NONE.
> > 
> > Duration GST_CLOCK_TIME_NONE is handled as duration = 0 in the sink, so it will
> > effectively 'fill time' up to the start timestamp.
> > 
> So streamsynchronizer should send GAP with duration is GST_CLOCK_TIME_NONE when
> received EOS for shorter track? and send GAP event for the shorter track to
> 'moves time forward' in other track chain function? And send all tracks EOS
> event after received all tracks EOS event.

Yes, if I've understood what you're trying to say correctly.

> > > > It looks like these patches will achieve that, but sending a new GAP when
> > > > changing state in stream synchroniser, but wouldn't it be better to wake the
> > > > streams that are looping on EOS every time data passes on another pad that
> > > > 'moves time forward' and have them send another GAP event then?
> > > > 
> > > streamsynchronizer will send GAP event in chain function. But pad push will be
> > > blocked when sink changed to PAUSED state and then can't send GAP event. So
> > > need send GAP event when state change in streamsynchronizer.
> > 
> > If pad_push() is blocked, you can't send a GAP event downstream there anyway -
> > the STREAM_LOCK() will be held by the pad_push(). That case should work. The
> > one that is broken is where you sent a GAP downstream from teh EOS event
> > handler, advancing time to some time 'timestamp', and then time advanced past
> > that point, and your pad_push_event (pad, gap_event) returned - you need to
> > either push a new gap, or push a buffer then.
> 
> If pad_push() is blocked, streamsynchronizer can't send GAP event for shorter
> track in chain function. The shorter track is reach EOS, and no any buffer
> reach streamsynchronizer, so basesink can't finished pre-roll for the shorter
> track. The longer track is pro-rolled and blocked. So streamsynchronizer need
> send GAP event for shorter track when state change, and send the GAP event in
> event handle thread.

There are 2 threads / streams in stream synchroniser:

* Short stream, has reached EOS. It pushes a gap downstream using gst_pad_push_event(), which won't return until the end time (timestamp + duration) of the gap event. Once that returns, it is free to push another gap event - or stop pushing them if it's time to switch to another input.
* The longer stream, buffers are still passing, and can be used to set timestamps on the gap events being sent in the other thread.

Is that clearer?
Comment 97 kevin 2015-02-05 15:40:38 UTC
Very clear. Thank you very much.
Comment 98 Sebastian Dröge (slomo) 2015-02-13 08:50:42 UTC
Review of attachment 295409 [details] [review]:

Looks good, just some minor things that should be fast to update :) Then this should be ready to be merged

::: gst/playback/gststreamsynchronizer.c
@@ +207,3 @@
+/* must be called with the STREAM_SYNCHRONIZER_LOCK */
+static gboolean
+gst_stream_synchronizer_wait (GstStreamSynchronizer * self, GstPad * pad)

Can this function also be used for the g_cond_wait() in the SEGMENT event handler? I think it should be used there too for consistency (and modified to work in both cases).

@@ +445,3 @@
       GST_STREAM_SYNCHRONIZER_LOCK (self);
       stream = gst_pad_get_element_private (pad);
+      stream->flushing = TRUE;

See the "if (stream)" some lines below :) You need to check for NULL

@@ +550,3 @@
+
+      /* send eos if haven't seed data as sink can't handle if haven't negotiate */
+      if (!seen_data || self->eos) {

The only case when we get here and this is not true, is when we're flushing? Document that and maybe even add an assertion :)

@@ +904,3 @@
+        if (stream->is_eos && !stream->eos_sent) {
+          self->send_gap_event = TRUE;
+          g_cond_broadcast (&stream->stream_finish_cond);

Please document in a comment here why a GAP event should be sent, also why not another one is required when going from PAUSED to PLAYING
Comment 99 Sebastian Dröge (slomo) 2015-02-13 08:52:51 UTC
Review of attachment 295410 [details] [review]:

::: gst/playback/gststreamsynchronizer.c
@@ +909,3 @@
         if (stream->is_eos && !stream->eos_sent) {
           self->send_gap_event = TRUE;
+          stream->gap_duration = GST_CLOCK_TIME_NONE;

This should probably also be reset in PAUSED->READY (where the segment is already reset currently).
Comment 100 Sebastian Dröge (slomo) 2015-02-13 08:56:38 UTC
Review of attachment 295411 [details] [review]:

::: tests/check/pipelines/streamsynchronizer.c
@@ +37,3 @@
+
+      g_mutex_lock (&test_mutex);
+      if (gst_event_parse_group_id (event, &group_id)) {

This should always succeed here, just add an assertion instead of the else case

@@ +126,3 @@
+      GST_STATE_CHANGE_FAILURE);
+
+  g_usleep (1 * G_USEC_PER_SEC);

Why the sleeps?

@@ +131,3 @@
+
+  g_object_unref (bus);
+  g_object_unref (G_OBJECT (pipeline));

Use gst_object_unref() and gst_object_ref(), and the G_OBJECT() casts are not needed then

@@ +144,3 @@
+  run_streamsynchronizer_handle_eos ("videotestsrc name=videosrc ! "
+      "streamsynchronizer name=streamsync audiotestsrc name=audiosrc num-buffers=0 ! "
+      "streamsync. streamsync. ! fakesink sync=true streamsync. ! fakesink sync=true");

Make a separate testcase for each of these pipelines. That will make the test runner output more informative :)

@@ +156,3 @@
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, test_streamsynchronizer_handle_eos);

Maybe we should also add some tests for the normal behaviour of streamsynchronizer while we're at it. Testing that it passes through data correctly, and especially aligns stream starts when they're switched to a new group.
Comment 101 kevin 2015-02-13 10:14:30 UTC
Thanks, I will updated based on the review comments and upload patch later.

Do you think aligns stream starts when they're switched to a new grout has some link with: https://bugzilla.gnome.org/show_bug.cgi?id=743973
Comment 102 Sebastian Dröge (slomo) 2015-02-13 10:26:50 UTC
Maybe, but for normal streams that should currently work... and having tests for that would be good :)
Comment 103 kevin 2015-02-15 03:20:17 UTC
(In reply to Sebastian Dröge (slomo) from comment #98)
> Review of attachment 295409 [details] [review] [review]:
> 
> Looks good, just some minor things that should be fast to update :) Then
> this should be ready to be merged
> 
> ::: gst/playback/gststreamsynchronizer.c
> @@ +207,3 @@
> +/* must be called with the STREAM_SYNCHRONIZER_LOCK */
> +static gboolean
> +gst_stream_synchronizer_wait (GstStreamSynchronizer * self, GstPad * pad)
> 
> Can this function also be used for the g_cond_wait() in the SEGMENT event
> handler? I think it should be used there too for consistency (and modified
> to work in both cases).
> 
Accept, will do it in another patch.
> @@ +445,3 @@
>        GST_STREAM_SYNCHRONIZER_LOCK (self);
>        stream = gst_pad_get_element_private (pad);
> +      stream->flushing = TRUE;
> 
> See the "if (stream)" some lines below :) You need to check for NULL
> 
Accept.
> @@ +550,3 @@
> +
> +      /* send eos if haven't seed data as sink can't handle if haven't
> negotiate */
> +      if (!seen_data || self->eos) {
> 
> The only case when we get here and this is not true, is when we're flushing?
> Document that and maybe even add an assertion :)
> 
Add more comments for seen_data.
> @@ +904,3 @@
> +        if (stream->is_eos && !stream->eos_sent) {
> +          self->send_gap_event = TRUE;
> +          g_cond_broadcast (&stream->stream_finish_cond);
> 
> Please document in a comment here why a GAP event should be sent, also why
> not another one is required when going from PAUSED to PLAYING
Add comments on why send GAP event. PAUSED to PLAYING needn't pre-roll, needn't send GAP event in change_state (). chain () will send GAP event in PLAYING state.
Comment 104 kevin 2015-02-15 03:32:02 UTC
(In reply to Sebastian Dröge (slomo) from comment #99)
> Review of attachment 295410 [details] [review] [review]:
> 
> ::: gst/playback/gststreamsynchronizer.c
> @@ +909,3 @@
>          if (stream->is_eos && !stream->eos_sent) {
>            self->send_gap_event = TRUE;
> +          stream->gap_duration = GST_CLOCK_TIME_NONE;
> 
> This should probably also be reset in PAUSED->READY (where the segment is
> already reset currently).

Do you mean set value to stream->gap_duration? The stream->gap_duration only used when self->send_gap_event is TRUE. So need set value to stream->gap_duration when set TRUE to self->send_gap_event.
Not accept currently:)
Comment 105 kevin 2015-02-15 04:24:40 UTC
(In reply to Sebastian Dröge (slomo) from comment #100)
> Review of attachment 295411 [details] [review] [review]:
> 
> ::: tests/check/pipelines/streamsynchronizer.c
> @@ +37,3 @@
> +
> +      g_mutex_lock (&test_mutex);
> +      if (gst_event_parse_group_id (event, &group_id)) {
> 
> This should always succeed here, just add an assertion instead of the else
> case
> 
Accepted.
> @@ +126,3 @@
> +      GST_STATE_CHANGE_FAILURE);
> +
> +  g_usleep (1 * G_USEC_PER_SEC);
> 
> Why the sleeps?
> 
Don't need it. Removed.
> @@ +131,3 @@
> +
> +  g_object_unref (bus);
> +  g_object_unref (G_OBJECT (pipeline));
> 
> Use gst_object_unref() and gst_object_ref(), and the G_OBJECT() casts are
> not needed then
> 
Accepted.
> @@ +144,3 @@
> +  run_streamsynchronizer_handle_eos ("videotestsrc name=videosrc ! "
> +      "streamsynchronizer name=streamsync audiotestsrc name=audiosrc
> num-buffers=0 ! "
> +      "streamsync. streamsync. ! fakesink sync=true streamsync. ! fakesink
> sync=true");
> 
> Make a separate testcase for each of these pipelines. That will make the
> test runner output more informative :)
> 
Accepted.
> @@ +156,3 @@
> +
> +  suite_add_tcase (s, tc_chain);
> +  tcase_add_test (tc_chain, test_streamsynchronizer_handle_eos);
> 
> Maybe we should also add some tests for the normal behaviour of
> streamsynchronizer while we're at it. Testing that it passes through data
> correctly, and especially aligns stream starts when they're switched to a
> new group.
Maybe can add the test when fix segment start which from different source is different:)
Comment 106 kevin 2015-02-15 09:10:24 UTC
Created attachment 296857 [details] [review]
streamsynchronizer: Send GAP event to finish preroll when change state from PLAYING to PAUSED
Comment 107 kevin 2015-02-15 09:11:01 UTC
Created attachment 296858 [details] [review]
streamsynchronizer: Send GAP event in event process thread, not data thread
Comment 108 kevin 2015-02-15 09:11:35 UTC
Created attachment 296859 [details] [review]
streamsynchronizer: Unit test for streamsynchronizer handle eos
Comment 109 kevin 2015-02-15 09:11:59 UTC
Created attachment 296860 [details] [review]
streamsynchronizer: Optimize wait when handle SEGMENT event
Comment 110 kevin 2015-02-15 09:31:55 UTC
I don't understand the group in streamsynchronizer. Don't know the scenarios which group used. Don't know when need wait in SEGMENT event. So not sure if the last patch is OK.
Comment 111 Sebastian Dröge (slomo) 2015-02-16 09:58:53 UTC
Review of attachment 296860 [details] [review]:

::: gst/playback/gststreamsynchronizer.c
@@ -403,3 @@
-          stream = gst_pad_get_element_private (pad);
-          if (stream)
-            stream->wait = FALSE;

I think this code was backwards, and would be broken with your changes (and a bit broken before already).
It probably should be something like

while (stream->wait && !self->flushing) {
  g_cond_wait(...);
  stream = gst_pad_get_element_private (pad);
}

And the code that calls g_cond_broadcast() would set wait=FALSE before the broadcast. Can you fix that when moving the code?


This waiting is used you gaplessly play multiple files one after another in playbin. It would cause streamsynchronizer to wait playing the next file until all streams of the previous one are finished.
Comment 112 Sebastian Dröge (slomo) 2015-02-16 10:01:43 UTC
(In reply to kevin from comment #104)
> (In reply to Sebastian Dröge (slomo) from comment #99)
> > Review of attachment 295410 [details] [review] [review] [review]:
> > 
> > ::: gst/playback/gststreamsynchronizer.c
> > @@ +909,3 @@
> >          if (stream->is_eos && !stream->eos_sent) {
> >            self->send_gap_event = TRUE;
> > +          stream->gap_duration = GST_CLOCK_TIME_NONE;
> > 
> > This should probably also be reset in PAUSED->READY (where the segment is
> > already reset currently).
> 
> Do you mean set value to stream->gap_duration? The stream->gap_duration only
> used when self->send_gap_event is TRUE. So need set value to
> stream->gap_duration when set TRUE to self->send_gap_event.
> Not accept currently:)

Yes, it has no real effect... but it would seem cleaner to me to reset that variable at that point too :)
Comment 113 kevin 2015-02-16 10:41:05 UTC
(In reply to Sebastian Dröge (slomo) from comment #112)
> (In reply to kevin from comment #104)
> > (In reply to Sebastian Dröge (slomo) from comment #99)
> > > Review of attachment 295410 [details] [review] [review] [review] [review]:
> > > 
> > > ::: gst/playback/gststreamsynchronizer.c
> > > @@ +909,3 @@
> > >          if (stream->is_eos && !stream->eos_sent) {
> > >            self->send_gap_event = TRUE;
> > > +          stream->gap_duration = GST_CLOCK_TIME_NONE;
> > > 
> > > This should probably also be reset in PAUSED->READY (where the segment is
> > > already reset currently).
> > 
> > Do you mean set value to stream->gap_duration? The stream->gap_duration only
> > used when self->send_gap_event is TRUE. So need set value to
> > stream->gap_duration when set TRUE to self->send_gap_event.
> > Not accept currently:)
> 
> Yes, it has no real effect... but it would seem cleaner to me to reset that
> variable at that point too :)

Ok.
Comment 114 kevin 2015-02-16 11:13:35 UTC
(In reply to Sebastian Dröge (slomo) from comment #111)
> Review of attachment 296860 [details] [review] [review]:
> 
> ::: gst/playback/gststreamsynchronizer.c
> @@ -403,3 @@
> -          stream = gst_pad_get_element_private (pad);
> -          if (stream)
> -            stream->wait = FALSE;
> 
> I think this code was backwards, and would be broken with your changes (and
> a bit broken before already).
> It probably should be something like
> 
> while (stream->wait && !self->flushing) {
>   g_cond_wait(...);
>   stream = gst_pad_get_element_private (pad);
> }
> 
> And the code that calls g_cond_broadcast() would set wait=FALSE before the
> broadcast. Can you fix that when moving the code?
> 
> 
> This waiting is used you gaplessly play multiple files one after another in
> playbin. It would cause streamsynchronizer to wait playing the next file
> until all streams of the previous one are finished.

Ok, Have fixed it. Will update patch after some test.
Comment 115 Sebastian Dröge (slomo) 2015-02-16 11:17:38 UTC
You can also use gst-play-1.0 with the --gapless and multiple files for testing this btw. If you do that, first test with a few audio-only files. And only then with audio/video files. audio/video files doesn't seem to work reliable currently.
Comment 116 kevin 2015-02-16 11:24:47 UTC
Created attachment 296915 [details] [review]
streamsynchronizer: Send GAP event to finish preroll when change state from PLAYING to PAUSED
Comment 117 kevin 2015-02-16 11:25:18 UTC
Created attachment 296916 [details] [review]
streamsynchronizer: Send GAP event in event process thread, not data thread
Comment 118 kevin 2015-02-16 11:25:49 UTC
Created attachment 296917 [details] [review]
streamsynchronizer: Unit test for streamsynchronizer handle eos
Comment 119 kevin 2015-02-16 11:26:09 UTC
Created attachment 296918 [details] [review]
streamsynchronizer: Optimize wait when handle SEGMENT event
Comment 120 kevin 2015-02-16 11:31:41 UTC
I will go home now. And will have a holiday for Chinese New Year. So can't test gapless playback currently. And will go back to work on Feb. 26th:)
Comment 121 Sebastian Dröge (slomo) 2015-02-16 12:37:49 UTC
Thanks for the patches and your patience, I've merged them now and did some minor cleanup on the way. Enjoy your holidays :)

commit fb9ca25f7f0ee850f73b2c323a2c3a8e7d2d92fc
Author: Song Bing <b06498@freescale.com>
Date:   Sun Feb 15 13:51:36 2015 +0800

    streamsynchronizer: Use the same waiting function for EOS and stream switches
    
    Also improve the waiting condition for stream switches, which was assuming
    before that the condition variable will only stop waiting once when it is
    signaled. But the documentation says that there might be spurious wakeups.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655

commit eeea911c2e477cc51ed72ac5ce2da34caa4115e3
Author: Song Bing <b06498@freescale.com>
Date:   Mon Jan 26 11:14:13 2015 +0800

    streamsynchronizer: Unit test for streamsynchronizer's EOS handling
    
    Test that a pipeline can change from PLAYING to PAUSED and back in
    the following scenarios:
    1. One track reach EOS after pushed some buffers while another track
    still pushes buffers
    2. One track reach EOS without buffers while another track still pushes
    buffers
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655

commit 2614f80309bf37e65b30f0ea76d2acefba1d57c4
Author: Song Bing <b06498@freescale.com>
Date:   Mon Jan 12 17:40:25 2015 +0800

    streamsynchronizer: Send GAP events from the pads' streaming threads
    
    Change the GAP events that are currently sent from the chain function of
    the current pad to all other EOS pads. They should instead be sent from
    their own streaming threads.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655

commit 9f81931716b7f8fd29862f0bfa1a3891cb414bdc
Author: Song Bing <b06498@freescale.com>
Date:   Mon Jan 12 16:08:33 2015 +0800

    streamsynchronizer: Send GAP event to finish preroll when change state from PLAYING to PAUSED
    
    Wait in the event function when EOS is received until all pads are EOS
    and then forward the EOS event from each pads own event function.
    
    Also send a new GAP event for EOS pads from the event function whenever
    going from PLAYING->PAUSED by shortly waking up the GCond. This is needed
    to allow sinks to pre-roll again, as they did not receive EOS yet because
    we blocked that, but also will never get data again.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655
Comment 122 Tim-Philipp Müller 2015-02-24 14:17:56 UTC
The streamsynchronizer unit test sometimes fails for me now. I don't know if it's related to these patches or not, but I don't remember noticing it before.

tpm@xps:~/gst/master/gst-plugins-base/tests/check$ make pipelines/streamsynchronizer.forever
...
pipelines/streamsynchronizer.c:108:E:general:test_streamsynchronizer_track_without_data:0: (after this point) Test timeout expired
66%: Checks: 3, Failures: 0, Errors: 1
Comment 123 kevin 2015-02-25 02:17:30 UTC
I add the unit test to test the patches. I will check the issue.
Comment 124 kevin 2015-02-26 08:07:02 UTC
Unfortunately, I can't reproduce the fail after run the unit test hundreds times. I use Gstreamer latest code on master branch. run the test on i.MX6Q board.
Comment 125 Sebastian Dröge (slomo) 2015-02-26 08:33:33 UTC
Try running it on a normal Linux machine with a bit more CPU power :)

make pipelines/streamsynchronizer.forever
Comment 126 kevin 2015-02-27 07:09:33 UTC
Created attachment 298061 [details] [review]
patch to workaround unit test fail issue.

The root cause of unit test fail is basesink sink->have_preroll haven't set to false as PLAYING->PAUSED is right after PAUSED->PLAYING. The wait thread hasn's been scheduled.

Attach patch to workaround it.

I have little test the patch as my Linux work station has some issue:(. And I have to attend one meeting right now. Post the patch and will test more later.
Comment 127 kevin 2015-02-27 08:53:03 UTC
I have test the workaround. It works. So the issue should be basesink issue.
Comment 128 kevin 2015-02-27 08:56:36 UTC
Created attachment 298066 [details] [review]
Remove unnecessary ERROR message
Comment 129 kevin 2015-02-27 10:01:15 UTC
Do we need fix the unit test fail issue or use the workaround? The issue only appear when state change very quickly.
Comment 130 Sebastian Dröge (slomo) 2015-02-27 10:07:32 UTC
Instead of sleep() it would be better to actually wait for the condition to happen. Is there a bug in basesink here that this is working around, or is this just the unit test being racy?
Comment 131 kevin 2015-02-27 15:34:33 UTC
Created attachment 298095 [details] [review]
patch to fix unit test fail.
Comment 132 kevin 2015-02-27 15:43:53 UTC
Why still need add sleep:
1. audio source will generate one audio buffer with 23ms. sink can finish preroll even if streamsynchronizer don't send GAP event when PLAYING->PAUSED if no sleep.
2. audio source will generate on audio buffer with 23ms. The buffer will consumed after sleep 100ms. sink can't finish preroll if streamsynchronizer don't send GAP event when PLAYING->PAUSED.
Comment 133 Tim-Philipp Müller 2015-02-27 15:50:21 UTC
Comment on attachment 298095 [details] [review]
patch to fix unit test fail.

Adding random sleeps doesn't seem right. How do you know it will be long enough under all circumstances, on all kinds of machines?
Comment 134 kevin 2015-03-02 15:23:51 UTC
Still met unit test fail even if wait state change complete with function gst_element_get_state((). Per my analysis, it is basesink issue, I really don't know how to fix it. Can ask basesink owner help to check it? Thanks.
Comment 135 Sebastian Dröge (slomo) 2015-03-02 15:48:53 UTC
Can you provide a detailed analysis of the problem? What steps are happening where, and why is that wrong or how should it be instead?
Comment 136 kevin 2015-03-03 07:22:13 UTC
Created attachment 298368 [details] [review]
Don't need preroll if have prerolled when state change from PLAYING to PAUSED
Comment 137 kevin 2015-03-03 07:22:59 UTC
Created attachment 298369 [details] [review]
Should wait state change complete before start another state change.
Comment 138 kevin 2015-03-03 07:29:44 UTC
streamsynchronizer unit no fail anymore:)

But I still wonder how to wait pipeline consumed the only one audio buffer and then start to state change from PLAYING to PAUSED. I want to test below pipeline.

  run_streamsynchronizer_handle_eos ("videotestsrc name=videosrc ! "
      "streamsynchronizer name=streamsync audiotestsrc name=audiosrc num-buffers=1 ! "
      "streamsync. streamsync. ! fakesink sync=true streamsync. ! fakesink sync=true");
Comment 139 Jan Schmidt 2015-03-04 10:09:07 UTC
The usual way to wait for one buffer to pass is a blocking pad probe on the sink's sinkpad.
Comment 140 kevin 2015-03-04 10:13:22 UTC
The audio buffer has duration about 23ms. I want wait fakesink sync=TRUE to consume it. My mean is pipeline playing time is large then 23 ms. How to wait that?
Comment 141 kevin 2015-03-04 14:10:33 UTC
Can help to review attached patch? So I can close it if the patch is ok.
Comment 142 Wim Taymans 2015-03-04 14:22:06 UTC
The patch makes sense to me but I would like a basesink unit test to see what goes wrong.
Comment 143 kevin 2015-03-04 14:48:15 UTC
The basesink unit test is ok with my patch in my side. How to reproduce basesink unit test fail in you side?

kevin@kevin-Lenovo-B470e:~/dev/gstreamer/tests/check$ make libs/basesink.forever
Running suite(s): GstBaseSink
100%: Checks: 4, Failures: 0, Errors: 0
Running suite(s): GstBaseSink
100%: Checks: 4, Failures: 0, Errors: 0
Comment 144 kevin 2015-03-04 14:49:54 UTC
Sorry, I misunderstand. Ignore comment 143.
Comment 145 Wim Taymans 2015-03-04 15:02:53 UTC
yes, I would like to understand how it fails without the patch and a unit test would show that. Or is there something else that shows failing behaviour?
Comment 146 kevin 2015-03-04 15:23:00 UTC
Application call state change PAUSED->PLAYING and then PLAYING->PAUSED very quickly. state_change () in basesink will be called very quickly. pre-roll wait function seems haven't be scheduled between two state change call. The reproduce rate is low. Don't know other use case can show the same fail.
Comment 147 Wim Taymans 2015-03-04 16:02:21 UTC
and what happens if it fails? you don't get a message? things lock up?
Comment 148 kevin 2015-03-05 00:24:12 UTC
Yes. will hang in pre-roll wait () as async is FALSE and need_preroll is TRUE. Attached log for your reference.
Comment 149 kevin 2015-03-05 00:25:06 UTC
Created attachment 298587 [details]
Log for basesink hang issue.
Comment 150 kevin 2015-03-05 02:27:47 UTC
Sorry. From the log, basesink seems has right behavior. It will be blocked on gst_base_sink_wait_preroll () when PLAYING to PAUSED. Needn't any change in basesink for the issue.
The fix for synchronizer unit test fail is unit test code. Shouldn't wait for async-done event when the state change isn't async change. As some time PLAYING to PAUSED isn't async change.
Comment 151 Wim Taymans 2015-03-11 15:43:21 UTC
commit 4b174b14f51b4d405674c0d1054e82f5dbc82094
Author: Wim Taymans <wtaymans@redhat.com>
Date:   Wed Mar 11 16:36:29 2015 +0100

    basesink: clean up the need_preroll variable
    
    Based on patch from Song Bing <b06498@freescale.com>
    
    Don't just set the need_preroll flag to TRUE in all cases. When we
    are already prerolled it needs to be set to FALSE and when we go to
    READY we should not touch it. We should only set it to TRUE in other
    cases, like what the code above does.
    
    See https://bugzilla.gnome.org/show_bug.cgi?id=736655
Comment 152 Sebastian Dröge (slomo) 2015-03-11 15:49:13 UTC
commit 1942870257747290c0d64a09e1a0d25bd296f8c6
Author: Song Bing <b06498@freescale.com>
Date:   Tue Mar 3 15:18:04 2015 +0800

    streamsynchronizer: Should wait state change complete before start another state change
    
    Should wait state change complete before start another state change.
    Can't ensure can received async-done message when state change from PLAYING to PAUSED.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655

commit 7ce97c723c0d439770022167a00a02abea7a8c8c
Author: Song Bing <b06498@freescale.com>
Date:   Fri Feb 27 16:40:23 2015 +0800

    streamsynchronizer: Remove unnecessary ERROR message.
    
    Remove unnecessary ERROR message.
    Push GAP will fail as flushing. Needn't ERROR message.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655
Comment 153 kevin 2015-03-12 14:56:40 UTC
(In reply to Wim Taymans from comment #151)
> commit 4b174b14f51b4d405674c0d1054e82f5dbc82094
> Author: Wim Taymans <wtaymans@redhat.com>
> Date:   Wed Mar 11 16:36:29 2015 +0100
> 
>     basesink: clean up the need_preroll variable
>     
>     Based on patch from Song Bing <b06498@freescale.com>
>     
>     Don't just set the need_preroll flag to TRUE in all cases. When we
>     are already prerolled it needs to be set to FALSE and when we go to
>     READY we should not touch it. We should only set it to TRUE in other
>     cases, like what the code above does.
>     
>     See https://bugzilla.gnome.org/show_bug.cgi?id=736655

The patch has issue. sink can't pause after state change from PLAYING to PAUSED. Will upload basesink log later.
Reproduce step:
1. revert below commit. so unit test will hang on wait async-done event. and then unit test timeout.
2. check log of basesink. basesink hasn't been paused.

commit 1942870257747290c0d64a09e1a0d25bd296f8c6
Author: Song Bing <b06498@freescale.com>
Date:   Tue Mar 3 15:18:04 2015 +0800

    streamsynchronizer: Should wait state change complete before start another state change
    
    Should wait state change complete before start another state change.
    Can't ensure can received async-done message when state change from PLAYING to PAUSED.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=736655
Comment 154 kevin 2015-03-12 15:01:39 UTC
Created attachment 299209 [details]
basesink log for sink has been paused when state change from PLAYING to PAUSED.
Comment 155 kevin 2015-03-12 15:03:13 UTC
Sorry, should be: basesink log for sink hasn't been paused when state change from PLAYING to PAUSED.
Comment 156 kevin 2015-03-12 15:36:25 UTC
Reproduce: make pipelines/streamsynchronizer.forever until unit test timeout.
Comment 157 GstBlub 2017-05-18 17:11:43 UTC
I have a similar problem, if not the same (1.12.0).  It seems like the problem is a race condition when acting on the buffering messages emitted by queue2 and prerolling, in my case an appsink.

In my case it sometimes, but not always, gets stuck.  It happens when I get a buffering message from queue2 that the buffer dropped to 0%, and I take the pipeline to PAUSED state.  However, it seems that the streamsynchronizer's queue is blocked (in gst_base_sink_wait_preroll of the appsink) pushing out a buffer, and at the same time it can't accept any new buffer because it's deemed full.  But because it can't accept any more buffers, the queue2 is blocked as well, and so it never gets a change to send another buffering message, which then would trigger a state change back to PLAYING, which would unblock the preroll wait.


Comment 158 GstBlub 2017-05-25 17:46:25 UTC
Created attachment 352589 [details] [review]
queue2: Add buffer-full-time, buffer-full-buffers, buffer-full-size properties

I spent a good amount of time investigating this issue.  I have come to conclusion that buffering cannot be implemented as documented, without potentially running into this lockup issue on the state change to PAUSED.  It does not appear to be a bug, more a deficiency in gstreamer's architectural design, or assumptions made that shouldn't be made.  I'll explain why, later.  Here's what an application (in this case an audio player) will have to do to work around this issue reliably:

1. Create playbin as usual, but do NOT use GST_PLAY_FLAG_BUFFERING.  Note however, that playbin2 may still emit buffering messages (bug in gstplaysink.c).
2. Create a bin that contains a queue2 element (let's call it "bufferqueue").  This element then is followed by whatever you need, in my case an appsink.  If playing back (e.g. using some audio sink) you will most likely want the "sync" property of your sink is set to TRUE.  Be sure to set the "async" property to FALSE, we do NOT want the state change to PAUSED to be asynchronous (I'll explain later, why)!  Assign this bin to the "audio-sink" property of the playbin element.
3. The application should maintain a "target state" that indicates whether the user wants PLAYING or PAUSED.  This is important because the pipeline state may be at PAUSED due to buffering, but the buffering logic will need to bring it back to PLAYING once buffered up, but leave it at PAUSED if the user paused playback during buffering.  Be sure to handle the case where buffering was disabled (See #6 and #7.b)
4. Create a "begin_buffering()" function that will set your queue2 ("bufferqueue") properties to the following: use-buffering=TRUE, max-size-buffers=0, max-size-bytes=0, max-size-time=0, buffer-full-time=<some non-zero value indicating how much decoded audio you want to buffer>.  This effectively changes the "bufferqueue" to an unbounded queue that will accept as much data as it is handed, but will report a 100% fill level once the decoded audio reaches what you set for the buffer-full-time property (attached patch required).  The queue2 element should be in this state ONLY when the pipeline is at PAUSED state *AND* it is at PAUSED state due to buffering.
5. Create a "done_buffering()" function that will set your queue2 ("bufferqueue") properties to the following: use-buffering=TRUE, max-size-buffers=0, max-size-bytes=0, max-size-time=<non-zero value indicating how much decoded audio this queue should, max>, buffer-full-time=0.  This effectively changes the "bufferqueue" to a bounded queue that will not hold on to more decoded audio data than max-size-time indicates.  The queue2 element should be in this state whenever the pipeline is at any state other than PAUSED, or when it is at PAUSED due to the user requested pausing playback ("target state" indicating PAUSED) and if applicable, buffering completed.
6. Create a "disable_buffering()" function that will set your queue2 ("bufferqueue") properties to the following: user-buffering=FALSE, max-size-buffers=0, max-size-bytes=0, max-size-time=<non-zero value indicating how much decoded audio this queue should, max>, buffer-full-time=0.  This effectively changes the "bufferqueue" to a bounded queue that will not hold on to more decoded audio data than max-size-time indicates, and it will no longer emit buffering messages.
7. When handling buffering messages, the application needs to do the following:
7.a Check if GST_MESSAGE_SRC() of the buffering message is equal to the queue2 element you created ("bufferqueue"), ignore the message if it's coming from any other element
7.b You might want to check whether this is a live pipeline (buffer message indicates GST_BUFFERING_LIVE or the last pipeline state change to PAUSED returned GST_STATE_CHANGE_NO_PREROLL).  You might want to disable the buffering logic in this case and call your "disable_buffering()" function.  Be sure to record that fact so that your application will proceed to PLAYING (See #3) once at PAUSED, because you're never going to get any more buffering messages.
7.b If your application determines that buffering should begin (e.g. when 0% is reported and 100% has never been reached), call your "begin_buffering()" function and take the pipeline to PAUSED.
7.c If your application determines that buffering completed (e.g. when 100% is reported), call your "done_buffering()" function, and if the user's target state indicates PLAYING, then take the pipeline back to PLAYING.
8. When initially starting playback, call your "begin_buffering()" function, and then take the pipeline to PAUSED (so that you can check whether it's a live pipeline by checking for GST_STATE_CHANGE_NO_PREROLL). Do *NOT* take it directly to PLAYING.  Once at paused, if the user's "target state" indicates PLAYING and buffering has not started or been disabled, proceed to PLAYING.

Essentially, what you need is a queue2 element that is unbounded during buffering and bounded otherwise.  Also, you want this queue2 at the decoding phase.  Currently, playbin2, uridecodebin, etc have queues that emit buffering messages, but these messages don't really make sense.  E.g. uridecodebin has a queue2 that reports buffering of the original source (e.g. souphttpsrc), in my case that means this queue constantly jumps from 0% to 100% (depending on source) because it may read a large chunk of data (in one buffer) that instantly fills the queue2 and then once it pushed that buffer out it is instantly drained, reporting 0%.  The other problem with playbin2's buffering is that there is a queue in the playsink that reports buffering based on the decoded data.  And then there's also a multiqueue that may emit buffering messages.  And the application gets all of those, but none of these really make sense (at least not to me).

And here's where the design flaw comes into play.  Let's say you get a buffering message of 0%.  In my case it was because souphttpsrc is just pushing a e.g. 1 MB sized buffer of undecoded audio and the 500kb buffer it pushed before had just drained the queue2 emitting that message).  When the application takes the pipeline to PAUSED, any push to the sink element will block due to prerolling.  However, this 1 MB buffer that souphttpsrc is pushing is now producing a whole bunch of audio data, so it flows into the decoder and essentially fills up all the queues maintained by playbin2/playsink.  Now the first buffers are finally making it out of playbin2 and they hit the sink element, but because it's in PAUSED this push is now blocked.  So it's sitting there, waiting to goto PLAYING, but because the entire chain (up to and including the source) is blocked (because all the queues are filled), no more buffering messages get emitted.  The problem really is that all theses queues are bounded in size, so they're set to not exceed a certain amount of data, which is not really a problem unless you're going to PAUSED and hit the prerolling wait.  Then there's that chance that the entire chain all the way to the source element is blocked and it can't get out of this state.  The only solution to solve this situation reliably is a dedicated queue2 for buffering purposes, and it should probably be operating on the decoded data, which needs to be unbounded during buffering phases and bounded otherwise.  This allows everything prior to that queue to at least complete the push during buffering, regardless of how much data this raw source ballooned into.  Because a unbounded queue2 could never emit buffering messages with values that make sense, I had to enhance the logic to allow defining a fill level that ought to be considered 100%, despite the queue being able to accept more data.  This unbounded queue2 now may receive a large amount of buffer, but it allows it to emit a 100% e.g. in the middle of pushing that large 1 MB buffer downstream, which then kicks everything back to PLAYING, unblocking the preroll wait in the sink element.

Also, if "async" on the sink element is not set to FALSE then the state changed from PLAYING back to PAUSED (e.g. due to buffering) is not going to complete because it expects to be taken to PLAYING again.  This probably could be handled by the application, but it probably gets complicated/weird.

I hope this all makes sense.  I believe the documentation of how buffering should be implemented is completely insufficient and will not implement a buffering strategy that will work reliably.  Depending on source, timing, etc. it's almost guaranteed you'll run into this "deadlock" unless you implement buffering as outlined above.

Also, for me, I can make sense of this design problem easiest by thinking about a source element pushing a large amount of encoded raw data that will get decoded into a large amount of buffers and/or data (in time), enough to fill up the entire chain, blocking the push all the way up to the source while in the prerolling wait on the sink element that is in PAUSED.
Comment 159 Edward Hervey 2018-05-08 05:52:46 UTC
This is a more generic issue regarding buffering and playbin/decodebin.

Those new issues should be supported (and if not, fixed) with playbin3 and decodebin3.

GstBlub, can you double-check if your issue is still present with playbin3/decodebin3 ? If so, let's determine the key issues to be fixed.
Comment 160 GStreamer system administrator 2018-11-03 12:23:22 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/75.