News:

Latest versions:
Server plugin: 0.5.1
MVP dongle: 0.5.2
Raspberry Pi client: 0.5.2
Windows client: 0.5.2-1

Main Menu

Vomp protocol

Started by AndersF, September 02, 2008, 01:11:43

Previous topic - Next topic

AndersF

Hi,

I have tried to understand the protocol between Vomp Client and server. I've understood most of the control protocol like list channels, read timers, etc.
I've been able to start streaming live TV, but what is the type of the streamed data?

Below is a specification of the protocol as I've understood it. Maybe someone can fill in some details and add some notes reagarding how to access Live TV and recordings?

   Vomp Protocol for communication between Vomp Client and Server
        ===============================================================

Introduction
=============

The Video Disk Recorder (VDR http://www.cadsoft.de/vdr/) supports a
plugin architecture.
Vomp (http://www.loggytronic.com/vomp.php) is a client application
implementing  set-top box functionality running on a Hauppauge MVP box.
The Vomp client accesses the TV functionality running on the VDR server via an
application specific protocol. The server part of the protocol is implemented
as a plugin on the VDR server.

This document specifies the Vomp protocol.
It is mostly compiled by reverse engineering of vompclientrrproc.c of Vomp
server, version 0.3.0.

The Vomp protocol consists of the following parts:

* Server detection
* Request-Response protocol
* Streaming protocol
* Keep Alive protocol

Server detection uses UDP messaging, whereas Request/Response, Streaming and Keep Alive messages are exchanged over TCP connection. The TCP connection is denoted a Vomp channel in subsequent text.

Multiple clients are supported by the protocol as well as the Vomp server implementation.

Server detection
================

- client broadcasts UDP to Address 255.255.255.255. Source and destination ports are 3024. Payload is 4 octets "VOMP" = 0x56 0x4f 0x4d 0x50
- Server responds to Client Address UDP port 3024 (always regardless of source port in previous broadcast.) Payload is 12 octets containing server name. Padding is NULL.

NOTE!
Current Server (Version 0.3.0) does not support client and server on the same Linux
host because Server always replies to UDP port 3024, which is already bound to server. Solution is to respond to source UDP port.
Server patch:
      ds.send(ds.getFromIPA(),ds.getFromPort(), serverName, strlen(serverName));
instead of:
      ds.send(ds.getFromIPA(),3024, serverName, strlen(serverName));


- Client establishes a TCP connection to Server. Destination IP Address is taken from UDP Unicast packet from server. Destination TCP port is 3024.



Request-Response protocol
=========================

/* Packet format for a request:

4 bytes = Channel ID = 1 (Request/Response channel)
4 bytes = Request ID
4 bytes = Opcode
4 bytes = Length of the rest of the packet
? bytes = rest of packet. depends on packet
*/

Packet format for a Response:

4 bytes = channel ID = 1 (request/response channel)
4 bytes = request ID
4 bytes = length of the rest of the packet
? bytes = rest of packet. depends on packet


The following lists the functional capabilities of the request/response protocol

    Opcode
     1:     Verified
      Login();
      Login Request is 6 octets and contains the client MAC address
      Server responds with 4 octets Unix Time followed by 4 octets GMT offset in
      Unix Time.
     2: V
      GetRecordingsList();
      Response:
      ULONG DiskspaceTotal[MB]
      ULONG DiskspaceFree[MB]
      ULONG DiskSpaceUtilisation[%]
      List Recordings {
   ULONG StartTime [Unix time]
   String Name
   String Filename
   }
     3: V
      DeleteRecording();
      Request:
   String FileName
      Response:
   Reply ULONG { 4=recording_not_found, 3=recording_control_found,
      2=delete_failed,1=ok }
     5: V
      GetChannelsList();
      Request:
      Response:
      List Channel {
      ULONG Number
      ULONG Type (What is type ?)
      String Name
      }
     6:
      StartStreamingChannel();
      Start live TV streaming for Channel number.
   RequestId is used as handle for stream.
   The stream will contain a header according to specification of streaming
   protocol below.
      Request:
   ULONG channelNumber
      Response:
   Failure:
      ULONG 0
   Success:
      ULONG 1
     7:
      GetBlock();
      What is this for for ?
      Request:
   ULONG Position
   ULONG Amount
      Response:
   Failure:
      ULONG 0
   Success:
   Data Block
     8:
      StopStreaming();
      Stop current Streaming of Recording or Live TV .
      Request:
   -
      Response:
   ULONG 1
     9:
      StartStreamingRecording();
      Start Streaming of a recording identified by FileName.
      Request:
   String FileName
      Response:
   Success:
   Ulong LengthBytes
   ULONG LengthFrames
   Failure:
      NO response !
   
     10:
      GetChannelSchedule();
      Request:
   ULONG ChannelNumber
   ULONG StartTime
   ULONG Duration
      Response:
   Failure and no events found:
      Ulong 0
   List {
        ULONG   EventID
        ULONG   EventTime
        ULONG   EventDuration
        String  EventTitle
        String  EventSubTitle
        String  EventDescription
   }


     11:
      ConfigSave();
      Request:
   String Section
   String Key
   String Value
      Response:
   ULONG   { 1=ok, 0=error}
     12:
      ConfigLoad();
      Request:
   String Section
   String Key
      Response:
   Failure:
      Ulong 0
   Success:
      String Value
     13:
      ReScanRecording();
      This Opcode is Obsolete
     14: V
      GetTimers();
      Request:
      Response:
   ULONG   numTimers
   List Timers{
       ULONG   Active
       ULONG   Recording
       ULONG   Pending
       ULONG   Priority
       ULONG   Lifetime
       ULONG   ChannelNumber
       ULONG   StartTime
       ULONG   StopTime
       ULONG   Day
       ULONG   WeekDays
Weekdays;       bitmask, lowest bits: SSFTWTM  (the 'M' is the LSB)

       String   FileName
   }
     15: V
      SetTimer();
      Request:
   String "Flags Channel Day Start Stop, Priority, Lifetime FileName Aux
Flags: unsigned integer
enum eTimerFlags { tfNone      = 0,
                   tfActive    = 1,
                   tfInstant   = 2,
                   tfVps       = 4,
                   tfRecording = 8,
                   tfAll       = 65535,
                 };
Channel:
   integer|String
Day:
  // possible formats are:
  // 19
  // 2005-03-19
  // MTWTFSS
  // MTWTFSS@19
  // MTWTFSS@2005-03-19
Start,Stop HHMM Local time
Priority: 0-99
Lifetime: 0-99
Aux: String Optional ( What is this for? )

      Response:
   ULONG { 2=bad_timerstring, 1=timer_already_set, 0=ok}
     16:
      PositionFromFrameNumber();
      Request:
   ULONG frameNumber
      Response:
   Ulong Position (0 in case of failure)
     17:
      FrameNumberFromPosition()
      Request:
   ULONG Position
      Response:
   Ulong Frame (0 in case of failure)
     18:
      MoveRecording();
      Request:
      Response:
     19:
      GetIFrame();
      Request:
      ULONG frameNumber
      ULONG direction
      Response:
   Failure:
      Ulong 0
   Success:
      ULLONG filePosition
      ULONG frameNumber
      ULONG frameLength
   
     20:
      GetRecInfo();
      Request:
   String FileName
      Response:
   Failure:
      ULONG 0
   Success:
      ULONG   StartTime
      ULONG   StopTime
      ULONG   ResumePoint
      String   Summary
      ULONG   NumberOfChannels
      List Components {
           UCHAR   Stream
           UCHAR   Type
           String   Language
           String   Description
      }
     21:
      GetMarks();
      Request:
   String   FileName
      Response:
   Failure:
      ULONG 0
   Success:
   List Mark {
        ULONG   Position
   }
     22:
      GetChannelPids();
      Request:
   ULONG channelNumber
      Response:
   Failure:
      ULONG 0
   Success:
   ULONG vpid
   ULONG numberOfApids
   List {
        ULONG apid
        String language
   }
   ULONG numberOfDpids
   List {
        ULONG dpid
        String language
   }
   ULONG    numberOfSpids
   List {
        ULONG spid
        String language
        }
   ULONG   tpid
      
     23:
      DeleteTimer();
      Request:
   ULONG ChannelNumber
   ULONG Weekdays
   ULONG Day
   ULONG StartTime
   ULONG StopTime

      Response:
   ULONG { 10=ok,4=no_matching_timer, 3=unable_to_delete, 1=timers_being_edited }

   ULONG Position


Media protocol extensions
     30:
      GetMediaList();
  * media List Request:
  * 4 length
  * 4 VDR_GETMEDIALIST
  * 4 flags (currently unused)
  * n dirname
  * n+1 0
  * Media List response:
  * 4 length
  * 4 VDR_
  * 4 numentries
  * per entry:
  * 4 media type
  * 4 time stamp
  * 4 flags
  * 4 strlen (incl. 0 Byte)
  * string
  * 0
     31:
      GetPicture();
  * get image Request: = GetPicture
  * 4 flags (currently unused)
  * 4 x size
  * 4 y size
  * n filename
  * n+1 0
  * get image response:
  * 4 length
  * 4 VDR_GETIMAGE
  * 4 len of image

     32:
      GetImageBlock();
      Request:
   ULONG Position
   ULONG Amount
      Response:
   Failure:
      ULONG 0
   Success:
   Data Block
     33:
      GetLanguageList();
     34:
      GetLanguageContent();



Streaming protocol
=========================
Packet format for a stream packet:

4 bytes = Channel ID = 2 (stream channel)
4 bytes = Stream ID (from requestID)
4 bytes = Code ( 0 = stream, 1= stream_end)
4 bytes = Length of the stream data (=0 when stream_end)
? bytes = stream data


Keep Alive protocol
=========================
Keep Alive (KA) Channel:

Request:
4 bytes = Channel ID = 3
4 Bytes Timestamp

Response:
4 bytes = channel ID = 3
4 Bytes Timestamp ( Copied from request )






MartenR

Maybe one thing should be noted, usually the vomp protocoll changes from version to version and in the past seldom a protocoll version of an old vomp version was compatible to a newer version.
So it might be hard for a third party software to catch up with the protokoll changes.

Marten

muellerph

#2
As I reimplemented so far everything with Qt, I would like to state following "unusual" things (unusual means I had problems doing it the right way when I tried to implement it):

- Strings: Strings are not defined by length (in Qt strings begin with a length information). The length is identified by the trailing /0, so you need to parse the package till you find a /0. Strings are not Utf-8, they are latin or something similar - so each byte one characted (maybe someone else can define it clearly).

- Mediaprotocol: The mediaprotocol has another length information for their content. I see this as redundant, as the package already has a length information for custom data.

- Mediaprotocol in general: I assume with the new version (not yet adjusted to 0.3.0), which allows pictures and sound in parallel the protocol will change here again. Current protocol won't allow sound and picture in parallel (only one media file can be opened at the same time).

- Request/Response: If possible, I would change the protocol here a bit. I would always start with a length information. But this is e.g. not existing for keepAlive packages (they have length=8). So you always need to parse the package what type it is, and depending on the type you get the length at a different position and based on the length you then can evaluate if the full package already arrived at the socket or not (not means for me waiting for next package arriving).

- Keep alives: You need to send regularely keep alive packages, as long as you didn't send any request/response package within, hmm I think it was 10s. Just do it every 5s and you are fine. "Streaming" recordings or media files involves request/response packages, no keepAlive necessary. Only live-tv streaming needs keep alives.

>Maybe someone can fill in some details and add some notes reagarding how to access Live TV and recordings

Live-TV: You send the request package, which channel is to be streamed and then the server starts already streaming packages until you send the request package stopstreaming. The client now need to handle the packages, there is no exchange of request for the streamdata in between server/client.
As there is no exchange (request/response) you need to send keepAlives during streaming to keep the connection alive.
Do you need more info/details on that?


Recordings:
Recordings and Mediadata are very very similar - as both are just a request for a file transfers (they completely share the same protocol-only the "open" is different, the read is equal).

Basically it comes down to the point that you send a request package, which file you want to be transferred.

Then you just send request packages in order to get junks of data (pos, length). You don't need to stop here anything, just don't ask for more data and you are done. If you want a different file, just request to open the diifferent file.

Compare the request for these "files" with something like fileLength=fileOpen("path/filename") and the request for data with data=read(pos, length) where pos <  fileLength. pos > fileLength will cancel the connection, pos + length > fileLength is fine.

> but what is the type of the streamed data
Just the plain mpeg data (I assume) as it is coming from the DVB card. No encapsulation or whatever is done here. I just pass these data to Phonon alias Xine and it works.

If you have more questions, just let me know.

Chris

Brilliant work everyone ... I'm thinking about adding a wiki to the web site that can host documentation like this a little more permanently, so - good start!

As for the data you receive: For recording playback you get exactly what is in the ???.vdr files. As I understand it, this is Multiplexed PES. However, this is set to change as I believe the VDR developer has said VDR is to record TS in future.

For live TV streaming you get a TS stream containing all the individual streams for the requested TV channel.

avvdr

Hi all,
for the mediaplayer part I went away a bit from the fxied predefined packets (except for the media data itself). Instead I used an approach like in other comm - protocols with compatbile serializers and deserializers at both sides. I already proposed to take this over for the other parts too - but did not get any response.
For me the advantage would be, that there is no need for bit by bit fixing the protocol (as all know this rapidly changes - so the description would always be outdated - like this one ;-( ).
Additionally the versioning schema included will allow for easily extend the protocol in a compatible way, so that old and new versions on both sides could interact.
For details refer to the serialize.* and vdrcommand.h on the client and server side.

Summary:
For the media player even I don't really care about the internal structure of the packages, I simply test the serialize/deserialize methods of the classes and that's it.
This way it is difficult to describe the bytes...

Regards
Andreas