chrippa / python-librtmp

python-librtmp is a RTMP client library. It uses the implementation provided by librtmp via cffi.
BSD 2-Clause "Simplified" License
154 stars 33 forks source link

Timeout before end #20

Closed mic159 closed 9 years ago

mic159 commented 9 years ago

Hi there, i've been trying to download an RTMP stream using librtmp, but it seems to only download the first ~5MB then pauses for a bit and returns success. The same command using rtmpdump works just fine.

Reproduce: https://gist.github.com/mic159/b71a8efdad7f67916ff0 The rtmpdump command will be printed to test with.

OS: Ubuntu 14.10 24bit

Could you tell me if I'm doing something wrong?

ghost commented 9 years ago

You could try passing the timeout argument to librtmp.RTMP().

connection = librtmp.RTMP(host, playpath=file, swfurl=SWF_URL, timeout=60)

timeout – int, Timeout the session after num seconds without receiving any data from the server. The default is 30.

Documentation

EDIT:

librtmp returns FLV data, so video.mp4 should be video.flv.

with open('video.flv, 'wb') as fle:
    size = 0
    for block in download_video():
        size += len(block)
        fle.write(block)
        sys.stdout.write('{}      \r'.format(size))
mic159 commented 9 years ago

@norwack Thanks, increasing the timeout does help and it will now download the whole video. But, it takes ages! running the rtmpdump command is much faster. What can I do to the script to make it just as fast?

I have updated the gist with the timeout increased.

Python:

real    22m13.594s
user    0m2.697s
sys 0m0.852s

rtmpdump:

real    1m49.613s
user    0m0.863s
sys 0m0.789s

The filename on the 'playpath' ends with 'mp4', but you are correct, the container is actually 'flv'. The append option is for when opening the file multiple times, not for every write call.

ghost commented 9 years ago

@mic159 What did you set the timeout to? Try playing around with it a little.

mic159 commented 9 years ago

@norwack I set it to 60 like you suggested (check the gist). It seems like it gets a burst of 5MB at the start, then it pauses for 40 seconds then just starts drip feeding the rest of the content at a very slow speed. But with rtmpdump it goes at the full speed the whole time.

mic159 commented 9 years ago

@norwack I looked over rtmpdump and my theory is that i need to tell the server to send the whole video in the "buffer" using setBufferTimeMS just like rtmpdump does.

ghost commented 9 years ago

@mic159 Try setting the buffer parameter in librtmp.RTMP().

Something like this: connection = librtmp.RTMP(host, playpath=file, swfurl=SWF_URL, buffer=30000)

Note that 30000 is the default value, try to play around with it.

mic159 commented 9 years ago

@norwack Yes! That works!

But unfortunately it does not seem to let you change it after the stream has started, like what rtmpdump does.

I want to:

def download_video(file, host):
    connection = librtmp.RTMP(host, playpath=file)
    connection.connect()
    stream = connection.create_stream()

    # Grab first chunk
    data = stream.read(1024 * 2)
    yield data

    connection.set_option('buffer', int(stream.duration * 1000))

    # Read rest of file...

But it has no effect. It seems to have to be set before the create_stream method is called. like this:

def download_video(file, host):
    connection = librtmp.RTMP(host, playpath=file)
    connection.connect()
    connection.set_option('buffer', 1372137)
    stream = connection.create_stream()

    # Grab first chunk
    data = stream.read(1024 * 2)
    yield data

    # Read rest of file...

Is there a way to get the duration before reading the first chunk, or to be able to set the option after starting the stream?

ghost commented 9 years ago

I'm unsure, hopefully @chrippa can help out on this.

chrippa commented 9 years ago

I've added a option (enabled by default) to RTMP.create_stream which toggles the same buffer hack used by rtmpdump. You can also set the buffer manually with RTMPStream.update_buffer(ms) now.