bilibili / ijkplayer

Android/iOS video player based on FFmpeg n3.4, with MediaCodec, VideoToolbox support.
GNU General Public License v2.0
32.4k stars 8.1k forks source link

Wait for stream closed or switch url on the fly on IOS #225

Closed linked67 closed 8 years ago

linked67 commented 9 years ago

Hi,

I have a rtsp server that can stream only 1 stream on the same time.

When i try to call stop or shutdown, i have to wait around 10 or 15 seconds before i can init a new player. If i init the player before this time, the server say me that there is already a stream open.

What can i do either to switch on the fly without needing to init a new player or have a message that it's ok ? I don't see MPMoviePlaybackStateStopped, only MPMoviePlaybackStatePlaying or MPMoviePlaybackStatePaused.

It would be nice to not wait the 10 or 15s, with vlc i don't have this problem.

bbcallen commented 9 years ago

Not sure, can you provide some log with timestamp?

linked67 commented 9 years ago

The log i have in Xcode don't has timestamp, it comes from ffmpeg i think. I use this code to stop / start the player:

-(void)closePlayer{
for (UIView *subview in [self.movieView subviews]) {
    [subview removeFromSuperview];
}

if (self.player) {
    if ([self.player isPlaying] || [self.player isPreparedToPlay]) {
        [self.player stop];
    }
   // [self.player shutdown];
}
//[self removeMovieNotificationObservers];
}

- (void)start {
   NSURL *theMovieURL = [NSURL URLWithString:@"myurl" ];
    [IJKFFMoviePlayerController setLogReport:YES];
    self.player = [[IJKFFMoviePlayerController alloc] initWithContentURL:theMovieURL withOptions:nil];
    self.player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
    self.player.view.frame = self.view.bounds;

    self.view.autoresizesSubviews = YES;

    [self.movieView addSubview:self.player.view];

    [self installMovieNotificationObservers];

    [self.player prepareToPlay];
    [self.player play];
}

And i only see this on stop:

ijkmp_stop()
ijkmp_stop()=0
aout_close_audio()

The 10 or 15s come from me watching the clock after many try. As you can see, i have commented some part. When i don't call shutdown, it takes a lot more time to close the stream. Maybe 20s or 30s, i have to test more to find the exact timing.

And this is what i have when i try to open a stream too fast:

ijkmp_set_format_callback(0x1001e0b54, 0x1741d0d10)
ijkmp_set_format_callback()=void
ijkmp_set_auto_play_on_prepared(1) 
ijkmp_set_auto_play_on_prepared()=void
2015-05-12 14:16:15.905 myapp[24654:3688387] OK setup GL
ijkmp_ios_set_view(glView=0x154e54fe0)
ijkmp_ios_set_view(glView=0x154e54fe0)=void
ijkmp_set_overlay_format(I420(0x30323449))
ijkmp_set_overlay_format()=void
ijkmp_set_data_source(url="rtsp://theurl")
ijkmp_set_data_source(url="rtsp://theurl")=0
ijkmp_set_format_option(safe, 0)
ijkmp_set_format_option()=void
ijkmp_prepare_async() 
ijkmp_prepare_async()=0
ijkmp_start()
ijkmp_start()=-3
[rtsp @ 0x1550b7c00] SDP:
v=0
o=leCDN 1431432975 1431432975 IN IP4 kapoueh.proxad.net
s=unknown
i=unknown
c=IN IP4 0.0.0.0
t=0 0
m=video 0 RTP/AVP 33
a=control:rtsp://theurl

[udp @ 0x154d25140] end receive buffer size reported is 65536
[udp @ 0x154d41da0] end receive buffer size reported is 65536
[rtsp @ 0x1550b7c00] method SETUP failed: 453 Not Enough Bandwidth
bbcallen commented 9 years ago

Change the line below in ff_ffplay.c to enable more log.

    av_lockmgr_register(lockmgr);
    if (g_ffmpeg_global_use_log_report) {
        av_log_set_callback(ffp_log_callback_report);
    } else {
        // av_log_set_callback(ffp_log_callback_brief);
        av_log_set_callback(ffp_log_callback_report);
    }

Or, would you please provide some playable stream for me to debug? I don't have much idea about rtsp.

VLC has its own rtsp implement. I think the problem could be in libavformat/rtsp.c

linked67 commented 9 years ago

I don't see more log like something that could happen after the delay where i can switch to another stream.

It would be great if you could test my stream but it's a media box under my TV and even if i setup everyting to give you access from outside, my upload bandwidth is too low.

Maybe you can test with some rtsp stream that we can easy find on the web ? They won't say you that a stream is already open because you switch to another URL and the new link will open with a new player instance while in the background the other is slowly closed. Maybe you can just catch what is going slow when a rtsp stream is closed.

Here is the first lisk i have found: rtsp://46.249.213.87/broadcast/bollywoodgossip-tablet.3gp

Let me know if you need more

bbcallen commented 9 years ago

Can't be reproduced with this url, maybe it's too slow from my network.

Can you pause the debug in xcode immediately after stop, to see where it is blocked in thread named "read_thread"?

linked67 commented 9 years ago

If you mean the ff_read thread, it's closed after 1s but not immediately after my close method call. I have put a sleep(1) and a breakpoint to see it.

If i put a sleep(30) between the close method and the new player init, it can play the second stream. With 25s in sleep, it doesn't work, it's too early.

I have notice that the only suspect thread i have see when the breakpoint stop just after the 30s sleep is: [self performSelectorOnMainThread:@selector(shutdownClose:) withObject:self waitUntilDone:YES];

It's alive before or after the 30s but dissapear once the new stream play. I have use your demo code to make the tests to have very few threads. The other threads open don't show something usefull (for me), only 2 lines like: __workq_kernelreturn start_wqthread

bbcallen commented 9 years ago

Modify the shutdown method as below, and see where it's blocked when you call shutdown.

- (void)shutdown
{
    if (!_mediaPlayer)
        return;
    [self unregisterApplicationObservers];
    [self setScreenOn:NO];

    ijkmp_stop(_mediaPlayer);
    ijkmp_shutdown(_mediaPlayer);
    ijkmp_dec_ref_p(&_mediaPlayer);
}
linked67 commented 9 years ago

After some other tests, sleep(29) is too early too while sleep(30) works.

I have take some screenshots to compare the different thread between 29 and 35 seconds after the close call and before i start to init a new player. With you modified shutdown, it's a bit more clear, there is less thread open.

The only difference bewteen a 5s and a 35s sleep is 2 thread with the same content:

Thread 5
__workq_kernreturn
start_wqthread

Thread 6
__workq_kernreturn
start_wqthread

I don't know what it means or how to use it. When i click on the first line (__workq_kernreturn) it display this:

libsystem_kernel.dylib`__workq_kernreturn: 0x11468c940 <+0>: movl $0x2000170, %eax 0x11468c945 <+5>: movq %rcx, %r10 0x11468c948 <+8>: syscall -> 0x11468c94a <+10>: jae 0x11468c954 ; <+20> 0x11468c94c <+12>: movq %rax, %rdi 0x11468c94f <+15>: jmp 0x114687c53 ; cerror_nocancel 0x11468c954 <+20>: retq
0x11468c955 <+21>: nop
0x11468c956 <+22>: nop
0x11468c957 <+23>: nop

The second (start_wqthread):

libsystem_pthread.dylib`start_wqthread: 0x1146bb400 <+0>: pushq %rbp 0x1146bb401 <+1>: movq %rsp, %rbp 0x1146bb404 <+4>: subq $0x18, %rsp 0x1146bb408 <+8>: callq 0x1146bd35e ; _pthread_wqthread 0x1146bb40d <+13>: leave
0x1146bb40e <+14>: retq
0x1146bb40f <+15>: nop

linked67 commented 9 years ago

Maybe the stream is closed on the device side and we see nothing interesting that remain but it looks like there is a "hey server, close the stream now" missing and the server just wait a 30s timeout before it close itself because there is no more traffic on this stream.

I'm not a network expert but it's the feel i have.

bbcallen commented 9 years ago

I'm not a network expert, too. :) Take a look at libavformat/rtsp.c.

linked67 commented 9 years ago

Well after some tests if i wait for a couple of seconds before i press stop and put a sleep(10), i no more see the thread 5 and 6. But if i resume it, the server still say me it's already open.

The open thread list is exaclty the same if i wait 10s or 35s. First case don't work, too early, second work. That make me think the same as my previous command, some command to validate the close should be missing before the phone close the stream.

nxtreaming commented 9 years ago

try this patch ( it's very old)

commit c973c2d9fa43b5f9dc1d0602b930e424971158ed

Author: GoogleGeek ffmpeg@gmail.com

Date: Fri Dec 26 19:27:22 2014 +0800

make stop operation ASAP

diff --git a/ijkmedia/ijkplayer/ff_ffplay.c b/ijkmedia/ijkplayer/ff_ffplay.c

index 0dec497..cae73cb 100644

--- a/ijkmedia/ijkplayer/ff_ffplay.c

+++ b/ijkmedia/ijkplayer/ff_ffplay.c

@@ -2551,6 +2551,11 @@ static int read_thread(void *arg)

         } else {

             ffp->error = 0;

         }

+

         if (eof) {

             ffp_toggle_buffering(ffp, 0);

             SDL_Delay(1000);

diff --git a/ijkmedia/ijksdl/android/android_audiotrack.c b/ijkmedia/ijksdl/android/android_audiotrack.c

index 5ae5768..9b7ebcc 100644

--- a/ijkmedia/ijksdl/android/android_audiotrack.c

+++ b/ijkmedia/ijksdl/android/android_audiotrack.c

@@ -442,6 +442,14 @@ void SDL_Android_AudioTrack_flush(JNIEnv env, SDL_Android_AudioTrack atrack)

void SDL_Android_AudioTrack_stop(JNIEnv env, SDL_Android_AudioTrack atrack)

{

 SDLTRACE("SDL_Android_AudioTrack_stop");

@@ -454,6 +462,7 @@ void SDL_Android_AudioTrack_stop(JNIEnv env, SDL_Android_AudioTrack atrack)

void SDL_Android_AudioTrack_release(JNIEnv env, SDL_Android_AudioTrack atrack)

{

 SDLTRACE("SDL_Android_AudioTrack_release");

On Wed, May 13, 2015 at 4:39 PM, linked67 notifications@github.com wrote:

Well after some tests if i wait for a couple of seconds before i press stop and put a sleep(10), i no more see the thread 5 and 6. But if i resume it, the server still say me it's already open.

The open thread list is exaclty the same if i wait 10s or 35s. First case don't work, too early, second work. That make me think the same as my previous command, some command to validate the close should be missing before the phone close the stream.

Reply to this email directly or view it on GitHub https://github.com/Bilibili/ijkplayer/issues/225#issuecomment-101575182.


Founder of http://LiveKings.com

linked67 commented 9 years ago

I have run instrument and have found that when i stop the stream at 10 seconds and just wait for allocations to move, something happens at 30s (only 20s more, weird). There is a few malloc and one has responsible caller: __net_helper_get_connection_block_invoke_3

And another: -[OS_xpc_connection _xref_dispose]

And another: _xpc_connection_dispose

It's in this order, maybe it can help you. Something is closed after a while.

I'll try your patch now.

linked67 commented 9 years ago

I just have add these lines to patch it since the other looks for android:

if (is->abort_request)
            break;

It does not change anything.

linked67 commented 9 years ago

You can test with the url i have give you before: rtsp://46.249.213.87/broadcast/bollywoodgossip-tablet.3gp

Use the demo code on github with this URL and replace the ClickOnPause method with this:

- (IBAction)onClickPause:(id)sender
{
[self closePlayer];
}

-(void)closePlayer{

    if (self.player) {
        if ([self.player isPlaying] || [self.player isPreparedToPlay]) {
            [self.player stop];
        }
        [self.player shutdown];
    }
    [self removeMovieNotificationObservers];

}

And comment the line 80 in IJKMediaControl.m to remove some noise because of the refresh timer. Now, start the project with allocating instrument. Record reference count and all alocations type.

Start the record, wait until 10s, the stream should be launched, and press the pause button twice because we have removed the refresh control. Let it run until 50s and see that something happens at 30s and 40s. We can see some allocation moving at 30s with xpc connection dispose but for 40s it display nothing even if i have see some move. Maybe a time glitch for this last.

This way you could test yourself and win some time.

I think the xpc connection that intrument talk about should dispose instantly instead of waiting so long. But i don't have deep understanding enough of this player to know where i should look to fix this.

bbcallen commented 9 years ago

You should look into libavformat/rtsp.c, FFmpeg does all the IO stuff.

linked67 commented 9 years ago

Have you find some time since to check it with any rtsp stream in the way i have say just above ?

bbcallen commented 9 years ago

Not, yet. Actually, not on top of my to-do list.

I think it's very likely an FFmpeg issue. Try ffplay on your pc directly.

linked67 commented 9 years ago

Well, still the same problem with latest build. Would be nice to find a solution to close the stream with no delay.

bbcallen commented 9 years ago

Add some log in libavformat/tcp.c, to see when and how it is closed.

linked67 commented 9 years ago

I have to use UDP connection, if i try this:

av_dict_set(&ffp->format_opts, "rtsp_transport", "tcp", 0);

The device that act as server say me it's unsupported. With this it's fine:

av_dict_set(&ffp->format_opts, "rtsp_transport", "udp", 0);

In other words, should i really log tcp.c file even with a UDP connection ?

bbcallen commented 9 years ago

Then log udp

bbcallen commented 8 years ago

Feel free to reopen if any more question.