Author: adrian
Date: Sun Jan 31 06:44:42 2010
New Revision: 8430

URL: http://svn.slimdevices.com/jive?rev=8430&view=rev
Log:
Bug: N/A
Description: update rtmp handler to include amf0 serialisation code so that 
applets can use the handler directly without server support

Modified:
    7.5/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Rtmp.lua

Modified: 7.5/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Rtmp.lua
URL: 
http://svn.slimdevices.com/jive/7.5/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Rtmp.lua?rev=8430&r1=8429&r2=8430&view=diff
==============================================================================
--- 7.5/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Rtmp.lua (original)
+++ 7.5/trunk/squeezeplay/src/squeezeplay/share/jive/audio/Rtmp.lua Sun Jan 31 
06:44:42 2010
@@ -7,9 +7,11 @@
 --
 -- (c) Triode, 2009, [email protected]
 --
--- The implementation here contains just the low level state machine for 
processing the rtmp protocol.
--- It relies on the server module to create serialised amf0 objects necessary 
to establish the stream and
--- returns amf0 response packets to the server for decoding.  It makes the 
following assumptions:
+-- The implementation (api v2) here contains both the low level state machine 
for processing the rtmp protocol and
+-- serialisation code for generating amf0 request objects.  Parsing of amf0 
responses is not implemented here and requires
+-- server support.
+--
+-- It makes the following assumptions:
 --
 -- 1) streams use a streamingId of 1 (it ignores the streamingId inside the 
amf0 _result reponse to a createStream message)
 -- 2) only implements single byte chunk headers (chunk id < 63)
@@ -18,7 +20,7 @@
 -- Due to the way the stream object is created it is necessary to switch 
methods in the stream object's meta table
 -- so that the Rtmp read and write methods here are used (this is done in 
Playback.lua)
 
-local string, table, math = string, table, math
+local string, table, math, pairs, type = string, table, math, pairs, type
 
 local Stream   = require("squeezeplay.stream")
 
@@ -28,6 +30,8 @@
 local log      = require("jive.utils.log").logger("audio.decode")
 
 module(...)
+
+local FLASH_VER  = "LNX 10,0,22,87"
 
 
 -- session params (can't be stored in the object as we reuse the streambuf 
object)
@@ -41,7 +45,8 @@
 
 function init(self, slimprotoObj)
        slimproto = slimprotoObj
-       slimproto:capability("Rtmp", 1)
+       -- api version 2 includes abilty to format and serialise amf objects
+       slimproto:capability("Rtmp", 2)
 end
 
 
@@ -60,6 +65,17 @@
 end
 
 
+local function packNumber(v, len, le)
+       local t = {}
+       for i = 1, len do
+               t[#t + 1] = string.char(v & 0xFF)
+               v = v >> 8
+       end
+       local str = table.concat(t)
+       return le and str or string.reverse(str)
+end
+
+
 local function changeState(newstate)
        log:info(state, " -> ", newstate)
        state = newstate
@@ -99,9 +115,19 @@
        ackWindow, nextAck, receivedBytes = 10240, 10240, 0
        state = "reset"
 
-       -- extract the pre built rtmp packets within the header
+       -- extract the pre built rtmp packets or params within the header
        for k, v in string.gmatch(header, "(%w+)=([a-zA-Z0-9%/%+]+%=*)&") do
                rtmpMessages[k] = mime.unb64("", v)
+       end
+
+       -- create serialised amf packets if params rather than prebuild packets 
extracted
+       if rtmpMessages["streamname"] then
+               rtmpMessages["create"]  = createStreamPacket()
+               rtmpMessages["play"]    = 
playPacket(rtmpMessages["streamname"], rtmpMessages["live"], 
rtmpMessages["start"])
+               rtmpMessages["connect"] = connectPacket(rtmpMessages["app"], 
rtmpMessages["swfurl"] or "", rtmpMessages["tcurl"])
+               if rtmpMessages["subname"] then
+                       rtmpMessages["subscribe"] = 
subscribePacket(rtmpMessages["subname"])
+               end
        end
 
        -- create the handshake token
@@ -270,14 +296,17 @@
                           end
 
                           if state ~= "Playing" then
+                                  if state ~= "Buffering" then
+                                          if rtmpMessages["meta"] ~= nil then
+                                                  slimproto:send({ opcode = 
"RESP", headers = "" })
+                                          end
+                                          changeState("Buffering")
+                                  end
                                   -- don't start playing live streams 
immediately as it causes stutter
                                   if rtmpMessages["subscribe"] and 
rtmp["timestamp"] < 4500 then
                                           return 0
                                   else
                                           changeState("Playing")
-                                          if rtmpMessages["meta"] ~= nil then
-                                                  slimproto:send({ opcode = 
"RESP", headers = "" })
-                                          end
                                   end
                           end
 
@@ -596,4 +625,131 @@
 end
 
 
-
+-- amf packet formatting and serialisation code
+-- this is added for api version 2 so we don't rely on server code to generate 
serialsed amf0
+
+-- emulate pack "d" to serialise numbers as doubles
+-- this only implements most signficant 28 bits of the mantissa, rest are 0
+function _todouble(v)
+       local s, e, m
+
+       if v == 0 then
+               return string.char(0, 0, 0, 0, 0, 0, 0, 0)
+       end
+
+       s = v > 0 and 0 or 1
+       v = v > 0 and v or -v
+       e = 0
+
+       local i = v
+       while i >= 2 do
+               i = i / 2
+               e = e + 1
+       end
+
+       m = v / math.pow(2, e - 28)
+       e = e + 1023
+
+       return string.char(
+               (s << 7) | 
+               ((e & 0x7f0) >> 4),
+               ((e & 0x00f) << 4) |
+               ((m & 0x0f000000) >> 24),
+                (m & 0x00ff0000) >> 16,
+                (m & 0x0000ff00) >>  8,
+                (m & 0x000000ff),
+               0, 0, 0)
+end
+
+
+function amfFormatNumber(n)
+       return string.char(0x00) .. _todouble(n)
+end
+
+
+function amfFormatBool(b)
+       return string.char(0x01, b and 0x01 or 0x00)
+end
+
+
+function amfFormatString(s)
+       return string.char(0x02) .. packNumber(string.len(s), 2) .. s
+end
+
+
+function amfFormatNull()
+       return string.char(0x05)
+end
+
+
+function amfFormatObject(o)
+       local res = string.char(0x03)
+       for k, v in pairs(o) do
+               res = res .. packNumber(string.len(k), 2) .. k
+               if type(v) == 'number' then
+                       res = res .. amfFormatNumber(v)
+               elseif type(v) == 'string' then
+                       res = res .. amfFormatString(v)
+               else
+                       res = res .. amfFormatNull()
+               end
+       end
+       return res .. string.char(0x00, 0x00, 0x09)
+end
+
+
+function formatRtmp(chan, type, streamId, body)
+       return
+               string.char(chan & 0x7f) ..        -- chan X, format 0
+               string.char(0x00, 0x00, 0x00) ..   -- timestamp (not 
implemented)
+               packNumber(string.len(body), 3) .. -- length
+               string.char(type & 0xff) ..        -- type
+               packNumber(streamId, 4, true) ..   -- streamId
+               body                               -- body
+end
+
+
+function connectPacket(app, swfurl, tcurl)
+       return formatRtmp(0x03, 20, 0,
+               amfFormatString('connect') ..
+               amfFormatNumber(1) ..
+               amfFormatObject({
+                       app         = app, 
+                       swfUrl      = swfurl,
+                       tcUrl       = tcurl,
+                       audioCodecs = 0x0404,
+                       videoCodecs = 0x0000,
+                       flashVer    = FLASH_VER
+               })
+       )
+end
+
+
+function createStreamPacket()
+       return formatRtmp(0x03, 20, 0,
+               amfFormatString('createStream') ..
+               amfFormatNumber(2) ..
+               amfFormatNull()
+       )
+end
+
+
+function subscribePacket(subscribe)
+       return formatRtmp(0x03, 20, 0,
+               amfFormatString('FCSubscribe') ..
+               amfFormatNumber(0) ..
+               amfFormatNull() ..
+               amfFormatString(subscribe)
+       )
+end
+
+
+function playPacket(streamname, live, start)
+       return formatRtmp(0x08, 20, 1, 
+               amfFormatString('play') ..
+               amfFormatNumber(0) ..
+               amfFormatNull() ..
+               amfFormatString(streamname) ..
+               amfFormatNumber(live and -1000 or (start or 0) * 1000)
+       )
+end

_______________________________________________
Jive-checkins mailing list
[email protected]
http://lists.slimdevices.com/mailman/listinfo/jive-checkins

Reply via email to