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 )
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
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.
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.
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