Audio/video stream recording forums (http://stream-recorder.com/forum/index.php)
-   rtmpdump (http://stream-recorder.com/forum/forumdisplay.php?f=54)
-   -  

librtmp and python

(http://stream-recorder.com/forum/showthread.php?t=11271)

Gontran 03-18-2012 08:33 AM

librtmp and python


 
Hi everybody,


since a few days I'm facing a very stressful issue with librtmp and python 2.7 (my os is Windows 7).

I'm trying to connect to a stream on justin tv but I've probably an encoding issue with jtv token string.

Here is my code:
Code:

        print 'instantiate rtmp connection'
        r = dll.RTMP_Alloc()
        dll.RTMP_Init(r)
        dll.RTMP_SetupURL(r, c_char_p(tcUrl))
       
        print 'connect to stream'
        dll.RTMP_Connect(r, None)
       
        print 'read stream'
        dll.RTMP_Read(r)

tcUrl looks like this:
tcUrl=rtmp://199.9.255.143/app/jtv_toTs9DzRvV8WPazW swfUrl=http://www-cdn.jtvnw.net/widgets/live_site_player.ra3170e7f148ff63b2f1b4dfef2840842 de54e7f8
.swf token=a6faa3f74e0927e80039bb612522d637530d6395:{\" swfDomains\": [\"justin.tv\", \"jtvx.com\", \"xarth.com\", \"twitchtv.com\", \"twitch.tv\", \"n
ewjtv.com\", \"jtvnw.net\", \"wdtinc.com\", \"imapweather.com\", \"facebook.com\", \"starcrafting.com\"], \"streamName\": \"jtv_toTs9DzRvV8WPazW\", \"
expiration\": 1332084704.643841, \"geo_ip\": \"82.243.172.91\", \"server\": \"lhr01-video3-2\"}

does anybody knows how to fix this?

Thanks

Regards,

Gontran

KSV 03-18-2012 10:53 AM

Re: librtmp and python


 
Quote:

Originally Posted by Gontran (Post 42169)
since a few days I'm facing a very stressful issue with librtmp and python 2.7 (my os is Windows 7).

I'm trying to connect to a stream on justin tv but I've probably an encoding issue with jtv token string.

You could have avoided that if you cared to read librtmp manpage. it clearly specifies
Code:

Special characters in values may need to be escaped to prevent
misinterpretation by the option parser. The escape encoding
uses a backslash followed by two hexadecimal digits representing
the ASCII value of the character. E.g., spaces must be escaped as \20
and backslashes must be escaped as \5c.

their are two problems with your code. first correct option is jtv not token which is for SecureToken. second escaping " as \" is only required on shell or cmd prompt. correctly encoded token may look like this.
Code:

jtv=a6faa3f74e0927e80039bb612522d637530d6395:{\22\20swfDomains\22:\20[\22justin.tv\22,\20\22jtvx.com\22,\20\22xarth.com\22,\20\22twitchtv.com\22,\20\22twitch.tv\22,\20\22newjtv.com\22,\20\22jtvnw.net\22,\20\22wdtinc.com\22,\20\22imapweather.com\22,\20\22facebook.com\22,\20\22starcrafting.com\22],\20\22streamName\22:\20\22jtv_toTs9DzRvV8WPazW\22,\20\22expiration\22:\201332084704.643841,\20\22geo_ip\22:\20\2282.243.172.91\22,\20\22server\22:\20\22lhr01-video3-2\22}

Gontran 03-18-2012 04:15 PM

Re: librtmp and python


 
Thank you KSV, you made my day.
Though I read the doc, I dont understand how I missed that!

Gontran 03-19-2012 07:15 PM

Re: librtmp and python


 
Now that I managed to retrieve a stream, I would like to get log from librtmp.
According to ctypes documentation and given functions signatures from log.h/log.c, I wrote this in my code:

Code:

CMPFUNC = CFUNCTYPE(c_int, c_int, c_char_p, c_void_p)
def logCallBack(level, message, va_list):
    dll.RTMP_LogPrintf(message, va_list)
    return 0

mLogCallBack = CMPFUNC(logCallBack)
...
dll.RTMP_LogSetCallback(mLogCallBack)

but instead of printing a nice readable log, I got a dirty output:

Code:

Parsed host    : ☺Parsed app    : ☺connect to stream
ÄßGj♫¨', ... connected, handshaking?ßGj♥: Type Answer  : 27DBEC?ßGjä?┼♣: Server Uptime : 2612204?ßGj♥: FMS Version  : 2612204.2612100.488283845.4882
83530?ßGj♥: Handshaking finished....ÄßGj♫¨', handshaked│°Gj♀☻: fd=2614284, size=2614180Invoking ▒?'ä°Gj♀☻: fd=2618556`÷Gjá%&: server BW = 2618572ä°Gj♀
☻: fd=2618556o÷Gjá%&: client BW = 2618572 2618468ä°Gj♀☻: fd=2618556?÷Gj, received ctrl. type: 2618572, len: 2618468?÷Gj, Stream Begin 2618572ä°Gj♀☻: f
d=26185568÷Gj, received: chunk size change to 2618572ä°Gj♀☻: fd=2618556N÷Gj♣☺, received: invoke 2618572 bytes(object begin)Property: <|±'☺>Property: <
|±'☺>Property: <|±'OBJECT>(object begin)Property: <,´'☺>Property: <,´'☺>Property: <,´'☺>(object end)Property: <|±'OBJECT>(object begin)Property: <,´'☺
>Property: <,´'☺>Property: <,´'☺>Property: <,´'OBJECT>(object begin)Property: <▄ý'☺>(object end)Property: <,´'☺>Property: <,´'☺>(object end)(object en
d)~÷Gjmt;, server invoking <☺>~÷Gj, received result for method call <☺>│°Gj♀☻: fd=2616844, size=2616740sending ctrl. type: 0x27f264│°Gj♀☻: fd=2616828,
 size=2616724│°Gj♀☻: fd=2616828, size=2616724Invoking ?‗'UsherToken: ú?B☻,☺│°Gj♀☻: fd=2617164, size=2617060Invoking )¶'ä°Gj♀☻: fd=2618556N÷Gj↔, receiv
ed: invoke 2618572 bytes(object begin)Property: <|±'☺>Property: <|±'☺>Property: NULLProperty: <|±'☺>(object end)~÷Gj?⌂;, server invoking <☺>~÷Gj8¶;, r
eceived result for method call <☺>¶÷Gj, seekTime=2617308, stopTime=2617204, sending play: â─↑?♥â╚*?e¶╔├U?ýâý►SVW?¨?w►?E*ëE??F♦┴Ó☻?┌??↨│°Gj♀☻: fd=26171
64, size=2617060Invoking )¶'sending ctrl. type: 0x27f264│°Gj♀☻: fd=2616828, size=2616724ä°Gj♀☻: fd=2618556N÷Gj%, received: invoke 2618572 bytes(object
 begin)Property: <|±'☺>Property: <|±'☺>Property: NULLProperty: <|±'☺>(object end)~÷Gj?⌂;, server invoking <☺>~÷Gj, received result id 2617308 without
matching requestä°Gj♀☻: fd=2618556?÷Gj, received ctrl. type: 2618572, len: 2618468?÷Gj☺, Stream Begin 2618572ä°Gj♀☻: fd=2618556N÷Gjö, received: invoke
 2618572 bytes(object begin)Property: <|±'☺>Property: <|±'☺>Property: NULLProperty: <|±'OBJECT>(object begin)Property: <,´'☺>Property: <,´'☺>Property:
 <,´'☺>Property: <,´'☺>(object end)(object end)~÷Gj?t;, server invoking <☺>~÷Gj┴t;, onStatus: ☺ä°Gj♀☻: fd=2618556N÷Gj?, received: invoke 2618572 bytes
(object begin)Property: <|±'☺>Property: <|±'☺>Property: NULLProperty: <|±'OBJECT>(object begin)Property: <,´'☺>Property: <,´'☺>Property: <,´'☺>Propert
y: <,´'☺>Property: <,´'☺>Property: <,´'☺>(object end)(object end)~÷Gj?t;, server invoking <☺>~÷Gj┴t;, onStatus: ☺read stream
ä°Gj♀☻: fd=2618348N÷Gj,, received: notify 2618364 bytes(object begin)Property: <(§'☺>Property: <(§'OBJECT>(object begin)Property: <?‗'☺>(object end)(o
bject end)ä°Gj♀☻: fd=2618348ä°Gj♀☻: fd=2618348N÷Gj?♥, received: notify 2618364 bytes(object begin)Property: <(§'☺>Property: <(§'OBJECT>(object begin)P
roperty: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Property: <?‗'☺>Propert
y: <?‗'☺>Property: <?‗'☺>Property: <?‗'OBJECT>(object begin)Property: <?*'OBJECT>(object begin)Property: <8?'☺>Property: <8?'☺>Property: <8?'OBJECT>(o
bject begin)Property: <??'OBJECT>(object begin)Property: <?Ú'☺>(object end)(object end)Property: <8?'☺>(object end)Property: <?*'OBJECT>(object begin)
Property: <8?'☺>Property: <8?'☺>Property: <8?'OBJECT>(object begin)Property: <??'OBJECT>(object begin)Property: <?Ú'☺>(object end)(object end)Property
: <8?'☺>Property: <8?'☺>Property: <8?'☺>Property: <8?'☺>(object end)(object end)Property: <?‗'OBJECT>(object begin)Property: <?*'☺>Property: <?*'☺>Pro
perty: <?*'☺>Property: <?*'☺>Property: <?*'☺>Property: <?*'☺>Property: <?*'OBJECT>(object begin)Property: <8?'☺>Property: <8?'☺>Property: <8?'☺>(objec
t end)(object end)(object end)(object end)Metadata:  ☺                    ,±'  ☺                    ,±'  ☺                    ,±'  ☺
      ,±'  ☺                    ,±'  ☺                    ,±'  ☺                    ,±'  ☺                    ,±'  ☺                    ,±'  ☺
                  ,±'  ☺                    ,±'☺:  ☺                    î?'  ☺                    î?'☺:  ☺                    ý?'  ☺
      î?'  ☺                    î?'  ☺                    î?'☺:  ☺                    ý?'  ☺                    î?'  ☺                    î?'  ☺
                    î?'  ☺                    î?'☺:  ☺                    ▄´'  ☺                    ▄´'  ☺                    ▄´'  ☺
        ▄´'  ☺                    ▄´'  ☺                    ▄´'☺:  ☺                    î?'  ☺                    î?'  ☺                    î?'ä°
Gj♀☻: fd=2618348│°Gj♀☻: fd=2618172, size=2618068Invoking ı¸'

I also tried to print "message" in my logCallBack() function but it gave me strings without values:

Code:

%s, ... connected, handshaking
%s: Type Answer  : %02X
%s: Server Uptime : %d
%s: FMS Version  : %d.%d.%d.%d
%s: Handshaking finished....
%s, handshaked
%s: fd=%d, size=%d
Invoking %s
%s: fd=%d
%s: server BW = %d
%s: fd=%d
%s: client BW = %d %d
%s: fd=%d
%s, received ctrl. type: %d, len: %d
...

I've been trying for almost 5 hours, digging into the web looking for a solution unsuccessfully.
That 's why I'm asking for help.

Thanks in advance.

Regards,


Gontran

KSV 03-20-2012 05:01 AM

Re: librtmp and python


 
I am not a python coder so i can't provide you a definite answer. but what you are referring as message in your code is actually format string. va_list contains the actual data to be filled in format string to create complete string. you can't pass va_list to another function. try to use PyArg_VaParse.

Gontran 03-20-2012 08:11 AM

Re: librtmp and python


 
Thank you KSV for your concern but as far as I know, PyArg_VaParse has to be used in c programms.
I tried to reproduce the same code as shown in this thread (http://stream-recorder.com/forum/usi...n-t11025.html? but this doesn't work.

Anyway, I'll dig deeper tonight.

Regards,

Gontran

Gontran 03-20-2012 10:14 AM

Re: librtmp and python


 
Just don't pay attention to my last messages: I was tired and doing all wrong!

The only thing needed was to set the log level (by default nothing) in order to see logs in the standard output:
Code:

#That's all!!!!
dll.RTMP_LogSetLevel(c_int(6))

Anyway, after cleaning it, I will post my code in case of somebody is interressed.

Gontran 03-20-2012 05:04 PM

Re: librtmp and python


 
As I said earlier, here a little class that wraps librtmp in order to get streams:

Code:

# -*- coding: utf-8 -*-
import os
from ctypes import cdll, c_char_p, c_int, create_string_buffer, sizeof

class RTMP:
    """
    This class encapsulates librtmp.dll in order to connect to a RTMP server and get video stream.
    RTMPDUMP website : http://rtmpdump.mplayerhq.hu
    LIBRTMP manpage  : http://rtmpdump.mplayerhq.hu/librtmp.3.html
    """
   
    LogLevel = {
        "RTMP_LOGCRIT"      : c_int(0),
        "RTMP_LOGERROR"    : c_int(1),
        "RTMP_LOGWARNING"  : c_int(2),
        "RTMP_LOGINFO"      : c_int(3),
        "RTMP_LOGDEBUG"    : c_int(4),
        "RTMP_LOGDEBUG2"    : c_int(5),
        "RTMP_LOGALL"      : c_int(6)
    }
   
    def __init__(self, tcURL, output_file_path, log_level=LogLevel["RTMP_LOGINFO"], buffer_size=64 * 1024):
        # Load library
        if os.name == 'nt':
            self.lib = cdll.LoadLibrary('lib/librtmp.dll')
        elif os.name == 'posix':
            # TODO: load library for posix os
            pass
           
        # Initialize variables
        self.tcURL              = tcURL
        self.output_file_path  = output_file_path
        self.log_level          = log_level
        self.buffer_size        = buffer_size
   
    #************************************************************************
    # Wrap native library functions
       
    def Alloc(self):
        """Return a pointer to a new RTMP object"""
        return self.lib.RTMP_Alloc()
   
    def Init(self, r_pointer):
        """Init the RTMP object"""
        self.lib.RTMP_Init(r_pointer)
        return None
   
    def SetupURL(self, r_pointer, tcURL):
        """
        Setup the rtmp url
        The rtmp url format is of the form
        rtmp[t][e|s]://hostname[:port][/app[/playpath]]
        """
        self.lib.RTMP_SetupURL(r_pointer, c_char_p(tcURL))
        return None
   
    def Connect(self, r_pointer):
        """Established network connection"""
        self.lib.RTMP_Connect(r_pointer, None)
        return None
       
    def ConnectStream(self, r_pointer):
        """Established RTMP session"""
        self.lib.RTMP_ConnectStream(r_pointer, 0)
        return None
       
    def Read(self, r_pointer, buffer):
        """
        Reads bytes from the stream an write its into the buffer.
        Returns the number of bytes read
        When it returns 0 bytes, the stream is complete and may be closed
        """
        return self.lib.RTMP_Read(r_pointer, buffer, sizeof(buffer))
       
    def Close(self, r_pointer):
        """ Closes the connection """
        self.lib.RTMP_Close(r_pointer)
        return None
       
    def Free(self, r_pointer):
        """ Frees the session """
        self.lib.RTMP_Free(r_pointer)
        return None
       
    def LogSetLevel(self, log_level):
        """ Defines RTMP_LogLevel used by output """
        self.lib.RTMP_LogSetLevel(log_level)
        return None
       
    #************************************************************************
   
    def getVideoStream(self):
        """
        Get the video stream from the rtmp server
        """
        # Try to open the output file
        try:
            output_file = open(self.output_file_path, 'wb')
        except:
            print "Cannot open output file.\n Please check the output_file_path\n. Aborting."
            return None
           
        # Instantiate buffer
        buffer = create_string_buffer(self.buffer_size)
       
        # Set LogLevel
        self.LogSetLevel(self.log_level)
       
        # Setup RTMP connection
        r_pointer = self.Alloc()
        self.Init(r_pointer)
        self.SetupURL(r_pointer, self.tcURL)
       
        # Connect and establish session
        self.Connect(r_pointer)
        self.ConnectStream(r_pointer)
       
        # Read stream
        try:
            while True:
                # While result > 0, write buffer bytes into output_file
                result = self.Read(r_pointer, buffer)
                output_file.write(buffer[:result])
                if result == 0:
                    break
        except:
            # Handles exception in order to close session and file properly
            print "An exception occured. Ending session."
       
        # Ends session and closes output_file
        self.Close(r_pointer)
        self.Free(r_pointer)
        output_file.close()

In order to use it, just instantiate a new RTMP object and call getVideoStream():
Code:

# construct the url before as mentionned in librtmp manpage
m_rtmp = RTMP(tcUrl, 'out_file.flv')
m_rtmp.getVideoStream()

Feel free to improve it (I know it's not perfect) and post comments.
Hope this will be usefull!

Regards,

Gontran

newtonelectron 03-20-2012 06:24 PM

Re: librtmp and python


 
Quote:

Originally Posted by Gontran (Post 42294)
As I said earlier, here a little class that wraps librtmp in order to get streams:

Code:

# -*- coding: utf-8 -*-
import os
from ctypes import cdll, c_char_p, c_int, create_string_buffer, sizeof

class RTMP:
    """
    This class encapsulates librtmp.dll in order to connect to a RTMP server and get video stream.
    RTMPDUMP website : http://rtmpdump.mplayerhq.hu
    LIBRTMP manpage  : http://rtmpdump.mplayerhq.hu/librtmp.3.html
    """
   
    LogLevel = {
        "RTMP_LOGCRIT"      : c_int(0),
        "RTMP_LOGERROR"    : c_int(1),
        "RTMP_LOGWARNING"  : c_int(2),
        "RTMP_LOGINFO"      : c_int(3),
        "RTMP_LOGDEBUG"    : c_int(4),
        "RTMP_LOGDEBUG2"    : c_int(5),
        "RTMP_LOGALL"      : c_int(6)
    }
   
    def __init__(self, tcURL, output_file_path, log_level=LogLevel["RTMP_LOGINFO"], buffer_size=64 * 1024):
        # Load library
        if os.name == 'nt':
            self.lib = cdll.LoadLibrary('lib/librtmp.dll')
        elif os.name == 'posix':
            # TODO: load library for posix os
            pass
           
        # Initialize variables
        self.tcURL              = tcURL
        self.output_file_path  = output_file_path
        self.log_level          = log_level
        self.buffer_size        = buffer_size
   
    #************************************************************************
    # Wrap native library functions
       
    def Alloc(self):
        """Return a pointer to a new RTMP object"""
        return self.lib.RTMP_Alloc()
   
    def Init(self, r_pointer):
        """Init the RTMP object"""
        self.lib.RTMP_Init(r_pointer)
        return None
   
    def SetupURL(self, r_pointer, tcURL):
        """
        Setup the rtmp url
        The rtmp url format is of the form
        rtmp[t][e|s]://hostname[:port][/app[/playpath]]
        """
        self.lib.RTMP_SetupURL(r_pointer, c_char_p(tcURL))
        return None
   
    def Connect(self, r_pointer):
        """Established network connection"""
        self.lib.RTMP_Connect(r_pointer, None)
        return None
       
    def ConnectStream(self, r_pointer):
        """Established RTMP session"""
        self.lib.RTMP_ConnectStream(r_pointer, 0)
        return None
       
    def Read(self, r_pointer, buffer):
        """
        Reads bytes from the stream an write its into the buffer.
        Returns the number of bytes read
        When it returns 0 bytes, the stream is complete and may be closed
        """
        return self.lib.RTMP_Read(r_pointer, buffer, sizeof(buffer))
       
    def Close(self, r_pointer):
        """ Closes the connection """
        self.lib.RTMP_Close(r_pointer)
        return None
       
    def Free(self, r_pointer):
        """ Frees the session """
        self.lib.RTMP_Free(r_pointer)
        return None
       
    def LogSetLevel(self, log_level):
        """ Defines RTMP_LogLevel used by output """
        self.lib.RTMP_LogSetLevel(log_level)
        return None
       
    #************************************************************************
   
    def getVideoStream(self):
        """
        Get the video stream from the rtmp server
        """
        # Try to open the output file
        try:
            output_file = open(self.output_file_path, 'wb')
        except:
            print "Cannot open output file.\n Please check the output_file_path\n. Aborting."
            return None
           
        # Instantiate buffer
        buffer = create_string_buffer(self.buffer_size)
       
        # Set LogLevel
        self.LogSetLevel(self.log_level)
       
        # Setup RTMP connection
        r_pointer = self.Alloc()
        self.Init(r_pointer)
        self.SetupURL(r_pointer, self.tcURL)
       
        # Connect and establish session
        self.Connect(r_pointer)
        self.ConnectStream(r_pointer)
       
        # Read stream
        try:
            while True:
                # While result > 0, write buffer bytes into output_file
                result = self.Read(r_pointer, buffer)
                output_file.write(buffer[:result])
                if result == 0:
                    break
        except:
            # Handles exception in order to close session and file properly
            print "An exception occured. Ending session."
       
        # Ends session and closes output_file
        self.Close(r_pointer)
        self.Free(r_pointer)
        output_file.close()

In order to use it, just instantiate a new RTMP object and call getVideoStream():
Code:

# construct the url before as mentionned in librtmp manpage
m_rtmp = RTMP(tcUrl, 'out_file.flv')
m_rtmp.getVideoStream()

Feel free to improve it (I know it's not perfect) and post comments.
Hope this will be usefull!

Regards,

Gontran

This script will fail misserably on linux in python 2 or 3.

I started work on something similar about a month ago and I'm surpirsed your script works.
I had to define custom ctype structure & union classes for my script, other wise it would complain about segmentation faults.
I am running linux though, so maybe windows doesn't care as much.

reumb 03-21-2012 07:34 AM

Re: librtmp and python


 
gontran,i see your ip on 1 post or not?


All times are GMT -6. The time now is 05:14 AM.