Page 1 of 1

Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 28 Jan 2015 03:11
by oni0n
I have been struggling with this for a few days. I think it's a bug but it's over my head to pin down exactly what's happening.

We have an app using libvlc to play TV streams. In this case it is playing an MRL like "dvb-t://frequency=177500000". This generally works but there's an annoying case, with the new tv tuner driver we have, where libvlc will stop playing any streams (tv or not) until you restart the app. To reproduce it, I:
1) Start trying to play a BOGUS frequency, i.e. one with no data on it.
2) Shortly after... call libvlc_media_player_stop(). The key is that it must be done quickly, within a couple seconds.
3) Now, no MRLs will play, including ones on the filesystem (e.g. foo.mpg).

I have gathered some info. When the problem happens, I can stop the program in gdb and see

(gdb) bt
#0 0x00132416 in __kernel_vsyscall ()
#1 0x016ad460 in poll () from /lib/i386-linux-gnu/libc.so.6
#2 0x045c3327 in dvb_read (d=0xae609b20, buf=0xae60afa0, len=3760) at dtv/linux.c:328
#3 0x045be610 in Read (access=0xae609aa8) at dtv/access.c:512
#4 0x017c75ee in AReadBlock (s=<optimized out>, pb_eof=0xb1236f9f) at input/stream.c:1725
#5 0x017c79ad in AStreamPrebufferBlock (s=0xae60a3c0) at input/stream.c:684
#6 0x017ca79a in stream_AccessNew (p_access=0xae609aa8, ppsz_list=0x0) at input/stream.c:378
#7 0x017be3a5 in InputSourceInit (p_input=0xb4727288, in=<optimized out>, psz_mrl=0xae83f718 "dvb-c://:frequency=330000000",
psz_forced_demux=0x0, b_in_can_fail=false) at input/input.c:2486
#8 0x017bf56f in Init (p_input=0xb4727288) at input/input.c:1225
#9 0x017c3a70 in Run (obj=0xb4727288) at input/input.c:521
#10 0x01488d4c in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
#11 0x016bbbae in clone () from /lib/i386-linux-gnu/libc.so.6


#0 0x00132416 in __kernel_vsyscall ()
#1 0x01489e1c in pthread_join () from /lib/i386-linux-gnu/libpthread.so.0
#2 0x01814e23 in vlc_join (handle=2971892544, result=0x0) at posix/thread.c:749
#3 0x017bd8e5 in input_Join (p_input=0xb4727288) at input/input.c:256
#4 0x017bd932 in input_Close (p_input=0xb4727288) at input/input.c:271
#5 0x0015ac9e in release_input_thread (p_mi=0xb4719290, b_input_abort=<optimized out>) at media_player.c:124
#6 0x0015d273 in libvlc_media_player_stop (p_mi=0xb4719290) at media_player.c:789 [this is where our app seems to start blocking. I notice after this point that no other commands are processed by our vlcworker object]
#7 0x0811e7b2 in VLCWorker::doStop (this=0x8343f40) at vlcworker.cpp:719
#8 0x08124069 in VLCWorker::doSetMrl (this=0x8343f40, mrl=...) at vlcworker.cpp:553
#9 0x08125f5e in VLCWorker::doCommand (this=0x8343f40, cmd=6, arg1=...) at vlcworker.cpp:110
#10 0x0815fedd in VLCWorker::qt_static_metacall (_o=0x8343f40, _c=QMetaObject::InvokeMetaMethod, _id=38, _a=0x9fb6cc0)
at moc_vlcworker.cpp:158
#11 0x0132c8e1 in QMetaCallEvent::placeMetaCall(QObject*) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#12 0x01335abb in QObject::event(QEvent*) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#13 0x006b2f74 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#14 0x006b82dd in QApplication::notify(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#15 0x0131a67e in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#16 0x0131e7d8 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
from /usr/lib/i386-linux-gnu/libQtCore.so.4
#17 0x0131eb0c in QCoreApplication::sendPostedEvents(QObject*, int) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#18 0x0134d2e4 in ?? () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#19 0x0204bd86 in g_main_context_dispatch () from /lib/i386-linux-gnu/libglib-2.0.so.0
#20 0x0204c125 in ?? () from /lib/i386-linux-gnu/libglib-2.0.so.0
#21 0x0204c201 in g_main_context_iteration () from /lib/i386-linux-gnu/libglib-2.0.so.0
#22 0x0134d6d7 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) ()
from /usr/lib/i386-linux-gnu/libQtCore.so.4
#23 0x01318f1d in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#24 0x013191b9 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#25 0x08121e9b in VLCWorker::mainLoop (this=0x8343f40) at vlcworker.cpp:83
#26 0x0815fe60 in VLCWorker::qt_static_metacall (_o=0x8343f40, _c=QMetaObject::InvokeMetaMethod, _id=32, _a=0xb50ff250)
at moc_vlcworker.cpp:152
#27 0x01331401 in QMetaObject::activate(QObject*, QMetaObject const*, int, void**) () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#28 0x013811c5 in QThread::started() () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#29 0x01203fe7 in ?? () from /usr/lib/i386-linux-gnu/libQtCore.so.4
#30 0x01488d4c in start_thread () from /lib/i386-linux-gnu/libpthread.so.0
#31 0x016bbbae in clone () from /lib/i386-linux-gnu/libc.so.6

And I've dug some more. It seems like dvb_read() isn't able to get any data (because it's a bogus frequency) and I think that's part of the problem. For some reason, libvlc_media_player_stop() isn't able to kill the input thread. It just keeps running. I added some print statements before the poll() call in the first backtrace and I can see the poll() line is running over and over again.

As noted, the problem doesn't occur if you wait a couple seconds, but that's not an option because the user can hit stop at any time.

I'm wondering if this is familiar to anyone. Why wouldn't libvlc_media_player_stop work? I took a stroll through input.c -> stream.c -> dtv/access.c -> dtv/linux.c but I didn't see anything obvious, but of course it gets abstract pretty quickly.

Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 28 Jan 2015 08:08
by Rémi Denis-Courmont
Did you register any callback with LibVLC? This problem is typical of a deadlock in there.

Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 30 Jan 2015 16:46
by oni0n
Hi Remi,

I think I narrowed it down to a race condition where the stream_t object gets created after the Stop button is pressed (which then calls libvlc_media_player_stop()), leaving an orphaned stream thread.

Further, what makes the situation special is that the TV tuner doesn't return anything when there's no data. This seems to make the stream thread get stuck in the "prebuffer" stage of the code (AStreamPrebufferBlock in the stack trace further down) or else I think that the stream thread would be able to terminate on its own.

I think I have a patch (below) that works in my case but I'm not sure if it's the right solution.:( The patch is against VLC 2.1.3

Code: Select all

--- a/src/input/input.c +++ b/src/input/input.c @@ -238,6 +238,7 @@ int input_Start( input_thread_t *p_input ) */ void input_Stop( input_thread_t *p_input, bool b_abort ) { + msg_Dbg(p_input, "input_Stop called"); /* Set die for input and ALL of this childrens (even (grand-)grand-childrens) * It is needed here even if it is done in INPUT_CONTROL_SET_DIE handler to * unlock the control loop */ @@ -2482,6 +2483,12 @@ static int InputSourceInit( input_thread_t *p_input, if( i_input_list > 0 ) TAB_APPEND( i_input_list, ppsz_input_list, NULL ); +if (!vlc_object_alive(p_input)) { + msg_Dbg(p_input, "p_input was killed, so not proceeding with creating stream module. aborting"); + access_Delete(p_access); + goto error; +} + /* Create the stream_t */ in->p_stream = stream_AccessNew( p_access, ppsz_input_list ); if( ppsz_input_list )
Some more notes on how I got the idea that it's the stream thread being left over.
I added print statements to ObjectKillChildrens, and noted that there was no "main stream debug: ObjectKillChildrens called" line when Stop was pressed too quickly (too quickly = within 2 seconds or so).

Code: Select all

== when valid stream is killed == [0xafb42a08] main input debug: input_Stop called [0xafb42a08] main input debug: ObjectKillChildrens called for 0xafb42a08 [0xafc8bf70] main decoder debug: ObjectKillChildrens called for 0xafc8bf70 [0xafc7b910] main packetizer debug: ObjectKillChildrens called for 0xafc7b910 [0xafc72c10] main decoder debug: ObjectKillChildrens called for 0xafc72c10 [0xafc113c8] main demux debug: ObjectKillChildrens called for 0xafc113c8 [0xafc08f08] main access debug: ObjectKillChildrens called for 0xafc08f08 [0xafc09ee0] main stream debug: ObjectKillChildrens called for 0xafc09ee0 [0xafc08d48] main stream debug: ObjectKillChildrens called for 0xafc08d48 [0xafb42a08] main input debug: control: stopping input [0xafb42a08] main input debug: ObjectKillChildrens called for 0xafb42a08 [0xafc8bf70] main decoder debug: ObjectKillChildrens called for 0xafc8bf70 [0xafc7b910] main packetizer debug: ObjectKillChildrens called for 0xafc7b910 [0xafc72c10] main decoder debug: ObjectKillChildrens called for 0xafc72c10 [0xafc113c8] main demux debug: ObjectKillChildrens called for 0xafc113c8 [0xafc08f08] main access debug: ObjectKillChildrens called for 0xafc08f08 [0xafc09ee0] main stream debug: ObjectKillChildrens called for 0xafc09ee0 [0xafc08d48] main stream debug: ObjectKillChildrens called for 0xafc08d48 == when bogus stream is killed too early == 8] main input debug: input_Stop called [0xb471c4c8] main input debug: ObjectKillChildrens called for 0xb471c4c8 [0xb3d207e8] main access debug: ObjectKillChildrens called for 0xb3d207e8 == when bogus stream is killed after sufficient waiting == [0xb470aad8] main input debug: input_Stop called [0xb470aad8] main input debug: ObjectKillChildrens called for 0xb470aad8 [0xb3d01360] main access debug: ObjectKillChildrens called for 0xb3d01360 [0xb3d09c38] main stream debug: ObjectKillChildrens called for 0xb3d09c38

Re: Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 02 Feb 2015 17:50
by Rémi Denis-Courmont
Then you might have found a bug.

Re: Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 21 Feb 2015 20:09
by Rémi Denis-Courmont

Re: Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 03 Apr 2015 14:56
by temak
I have faced the same issue on VLC 2.2.0

Re: Calling libvlc_media_player_stop() on DVB-T channel never returns

Posted: 25 May 2015 20:20
by oni0n
It has been awhile but I thought I should update. We have been using the patch above with success.

I think this is a very specific bug. I'm not sure if it effects other cases. I walked through it again and this is the process that I understand to be happening.

1. The user starts playing a new stream with

Code: Select all

libvlc_media_player_play()
.
2. The process to create an input thread is started.
3. The user hits stop quickly (calling libvlc_media_player_stop()), before the line `stream_t *p_stream = stream_AccessNew( p_access, ppsz_input_list );` (line 2486 of input/input.c) gets run.
4. As a result, `stream_AccessNew` runs but `!vlc_object_alive(s)` does NOT resolve to true because the object s was "killed" (marked dead) before it was created.
5. Now when the function AStreamPrebufferBlock call is run (line 378), it can get stuck in the `for ( ;; )` loop. The reason is as follows:

The only ways to get out the loop are:

Code: Select all

if( !vlc_object_alive(s) || p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE ) {
or

Code: Select all

if( ( b = AReadBlock( s, &b_eof ) ) == NULL ) { if( b_eof ) break;
That is:
-The object is killed/stopped. But as noted in step 4, !vlc_object_alive(s) won't resolve to true in this situation.
-p_sys->block.i_size > STREAM_CACHE_PREBUFFER_SIZE, i.e. enough data is read. But as I said, in this case, no data is being received from the tuner.
-`if( b_eof )` But, because no data is being received from the tuner, no EOF flag is being set either.

I looked a bit further and it looks like if it makes it past Init (in the Run function in input.c), it will then call MainLoop, which does have a `while (vlc_object_alive(p_input` check that should cause the MainLoop to then abort since at least p_input is marked dead.

Hopefully the long post adds clarity and not confusion.

Anyhow... I will try to try this against master and post a patch to the bug thread soon. That seems like the right thing to do here.