Audio/video stream recording forums

Attention Visitor:
You may have to register or log in before you can post:
  • Click the register link to sign up.
  • Registered members please fill in the form below and click the "Log in" button.
To start viewing messages, select the forum that you want to visit from the selection below.

Go Back   Audio/video stream recording forums > Streaming media recording forum > rtmpdump
Register FAQ Members List Calendar Mark Forums Read

Reply Post New Thread
 
Thread Tools Display Modes
  #1  
Old 03-18-2012, 09:33 AM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

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
Reply With Quote
  #2  
Old 03-18-2012, 11:53 AM
KSV KSV is offline
Senior Member
 
Join Date: Apr 2011
Posts: 853
KSV is on a distinguished road
Cool

Re: librtmp and python


Quote:
Originally Posted by Gontran View Post
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}
Reply With Quote
  #3  
Old 03-18-2012, 05:15 PM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

Re: librtmp and python


Thank you KSV, you made my day.
Though I read the doc, I dont understand how I missed that!
Reply With Quote
  #4  
Old 03-19-2012, 08:15 PM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

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

Last edited by Gontran : 03-20-2012 at 05:42 AM.
Reply With Quote
  #5  
Old 03-20-2012, 06:01 AM
KSV KSV is offline
Senior Member
 
Join Date: Apr 2011
Posts: 853
KSV is on a distinguished road
Cool

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.
Reply With Quote
  #6  
Old 03-20-2012, 09:11 AM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

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
Reply With Quote
  #7  
Old 03-20-2012, 11:14 AM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

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.
Reply With Quote
  #8  
Old 03-20-2012, 06:04 PM
Gontran Gontran is offline
Junior Member
 
Join Date: Mar 2012
Posts: 8
Gontran is on a distinguished road
Default

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
Reply With Quote
  #9  
Old 03-20-2012, 07:24 PM
newtonelectron newtonelectron is offline
Junior Member
 
Join Date: Mar 2012
Posts: 1
newtonelectron is on a distinguished road
Default

Re: librtmp and python


Quote:
Originally Posted by Gontran View Post
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.
Reply With Quote
  #10  
Old 03-21-2012, 08:34 AM
reumb reumb is offline
Junior Member
 
Join Date: Jul 2011
Posts: 13
reumb is on a distinguished road
Default

Re: librtmp and python


gontran,i see your ip on 1 post or not?
Reply With Quote
Reply Post New Thread
Tags: ,



Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT -6. The time now is 12:26 PM.


Powered by All-streaming-media.com; 2006-2011
vB forum hacked with Zoints add-ons