5 * RAWV - low-latency lossless video streaming over 1Gbps Ethernet
6 * Copyright (C) 2010 Kirill Smelkov <kirr@navytux.spb.ru>
8 * You may use and redistribute this work under the Creative Commons
9 * Attribution-Share Alike (CC-BY-SA) 3.0. You are free to Share (to copy,
10 * distribute, display, and perform the work) and to Remix (to make derivative
11 * works), under the following conditions:
13 * 1. Attribution. You must attribute the work in the manner specified by the
14 * author or licensor (but not in any way that suggests that they endorse you
15 * or your use of the work).
16 * 2. Share Alike. If you alter, transform, or build upon this work, you may
17 * distribute the resulting work only under the same, similar or a compatible
24 * A scheme for transmitting progressive PAL/NTSC video over 1Gbps Ethernet
25 * LAN is presented. Lossless coding, low end-to-end latencies (under 100ms in
26 * non-specialized test setup) and packet loss tolerance make this scheme
27 * applicable for specialized tasks in medical, industrial and military
28 * environments. Development effort for both software and hardware
29 * implementations is shown to be low because of design simplicity.
35 * - each frame is transmitted in series of independent fragments
36 * - each fragment carries header and payload
39 * 1. special 4-byte magic identifying this coding method,
40 * 2. version of coding subvariant and fragment flags,
41 * 3. fragment's frame sequence number,
42 * 4. fragment sequence number and number of fragments in the whole frame,
43 * 5. width, height and pixel format of its frame,
44 * 6. fragment startline and number of lines in this fragment,
48 * < - - - - - - w i d t h - - - - - - >
49 * +-----------------------------------+ ^
54 * frag_startline -->|-----------------------------------| h
58 * |-----------------------------------| h
64 * +-----------------------------------+ v
69 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
70 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71 * 0 | 0x52 ('R') | 0x41 ('A') | 0x57 ('W') | 0x56 ('V') |
72 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73 * 32 | Version | Flags | Frame Number |
74 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75 * 64 | Fragment Number | Fragments Total |
76 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 * 96 | Frame Width | Frame Height |
78 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79 * 128 | Pixel Format |
80 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81 * 160 | Fragment Startline | Fragment Lines |
82 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90 * Version of coding used. This specification describes coding for
94 * This field is currently reserved to be 0x00.
96 * Frame Number (nframe)
97 * Sequence number of this fragment's frame.
99 * Fragment Number (nfragment)
100 * Sequence number of this fragment. Starts from 0 for each new frame.
102 * Fragments Total (fragments_total)
103 * Total number of fragments in current frame.
105 * Frame Width (width)
106 * Frame width in pixels.
108 * Frame Height (height)
109 * Frame height in pixels.
111 * Pixel Format (pixfmt)
112 * 4 characters denoting payload pixel format according to
113 * http://fourcc.org/yuv.php; recommended are YUYV, YUY2, UYVY, GREY,
116 * Fragment Startline (frag_startline)
117 * Vertical position of this fragment's first line relative to frame top.
119 * Fragment Lines (frag_nlines)
120 * Number of lines in this fragment.
123 * all integer fields are carried in network byte order, that is, most
124 * significant byte (octet) first.
129 * 1. right after header go raw pixel data in specified-in-header pixel format.
130 * There should be data for at least
132 * frag_nlines * width (1)
136 * frag_nlines * width * pixsize(pixfmt) (2)
140 * 2. there could be more data than specified in (2). in such a case,
141 * trailer content is out of this specification scope.
144 * TODO talk about packet reordering
145 * TODO talk about packet loss
146 * TODO talk about needed bandwidth for typical streams
147 * (PAL color, PAL mono, NTSC color, NTSC mono)
152 * [1] RFC3550 -- RTP: A Transport Protocol for Real-Time Applications
153 * [2] RFC3551 -- RTP Profile for Audio and Video Conferences with Minimal Control
154 * [3] RFC4175 -- RTP Payload Format for Uncompressed Video
155 * [4] IIDC 1394-based Digital Camera Specification Ver.1.32
162 * Copyright (C) 2010,2011,2012 Kirill Smelkov <kirr@navytux.spb.ru>
163 * Copyright (C) 2011 Marine Bridge and Navigation Systems (http://mns.spb.ru/)
165 * This library is free software; you can redistribute it and/or
166 * modify it under the terms of the GNU Lesser General Public
167 * License as published by the Free Software Foundation; either
168 * version 2.1 of the License, or (at your option) any later version.
170 * This library is distributed in the hope that it will be useful,
171 * but WITHOUT ANY WARRANTY; without even the implied warranty of
172 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
173 * Lesser General Public License for more details.
175 * You should have received a copy of the GNU Lesser General Public
176 * License along with this library; if not, write to the Free Software
177 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
180 #include <asm/types.h>
181 #include <sys/types.h>
185 #include <arpa/inet.h>
196 /* compose 4-characters tag to a 32-bit integer
198 * The mapping is so that host native in-memory representation of resulting
199 * integer always repeats characters in originally specified order. e.g.
201 * MKTAG32('R','A','W','V') -> 0x56574152 (= "RAWV") LE
202 * -> 0x52415756 (= "RAWV") BE
204 * that is integer is in native order, by character go in network byte-order
207 #define MKTAG32_LE(a,b,c,d) (((__u32)(a)<< 0)|((__u32)(b)<< 8)|((__u32)(c)<<16)|((__u32)(d)<<24))
208 #define MKTAG32_BE(a,b,c,d) (((__u32)(a)<<24)|((__u32)(b)<<16)|((__u32)(c)<< 8)|((__u32)(d)<< 0))
210 #if __BYTE_ORDER != __BIG_ENDIAN
211 # define MKTAG32 MKTAG32_LE
213 # define MKTAG32 MKTAG32_BE
220 * all fields go in network byteorder on the wire(*)
222 #pragma pack(push, 1)
224 __u32 magic
; /* (*) fourcc -- _characters_ go in network byteorder */
227 __u8 __reserved_for_flags
;
231 __u16 fragments_total
;
236 __u32 pixfmt
; /* (*) fourcc -- _characters_ go in network byteorder */
238 __u16 frag_startline
;
241 /* padding, so that the whole header is 32 bytes */
251 size_t length
; /* ~= height * bytesperline */
253 int width
, height
, bytesperline
;
254 __u32 pixfmt_4cc
; /* characters go in network byteorder */
260 /* if we know top/bottom fields are in wrong order in this frame
263 * -1 -- swap and shift one filed XXX write more
265 signed int interlace_tb_swapped
: 2;
270 * Interfaces for video sink and source
273 /* Source will query framebuffer from one of subscribed sinks, and if ok,
274 * will put video frame directly into it. Otherwise source will use it's own
277 * ->v_query_framebuf() should read
283 * to see whether it is possible to provide a buffer for requested
284 * parameters, and if yes, have to set
292 * buffer should be valid until one next ->v_on_frame() call, after which
293 * buffer ownership returns back to sink.
294 * XXX interaction with v_unsubscribe()
296 virtual bool v_query_framebuf(Frame
*f
) = 0;
298 /* Source will notify sink when new frame is available via this method */
299 virtual void v_on_frame(const Frame
*f
) = 0;
302 virtual ~IVideoSink();
306 struct IVideoSource
{
307 virtual void v_start_capture() = 0;
308 virtual void v_stop_capture() = 0;
310 virtual void v_subscribe(IVideoSink
*) = 0;
311 virtual void v_unsubscribe(IVideoSink
*) = 0;
313 virtual ~IVideoSource();
317 /* convenience class for IVideoSource implementers */
318 struct VideoSource
: IVideoSource
{
319 void v_subscribe(IVideoSink
*);
320 void v_unsubscribe(IVideoSink
*);
323 vector
<IVideoSink
*> v_subscribers
;
324 IVideoSink
*vs_sinkbuf
; /* sink which provided framebuf */
326 /* go through subscribers & notify them */
327 void notify_v_subscribers(const Frame
*f
);
329 /* query sinks for video framebuffer for f's frame-metric.
331 * see IVideoSink->v_query_framebuf() for details.
333 bool query_sink_framebuf(Frame
*f
);
343 * Displays video frames to YUY2 overlay
345 struct View
: IVideoSink
347 View(int w
, int h
, bool upscale
, const char *title
);
350 /* whether to upscale frame on display.
352 * NOTE: only upscale is supported, not downscale
357 bool v_query_framebuf(Frame
*f
);
358 void v_on_frame(const Frame
*f
);
361 SDL_Surface
*display
;
362 SDL_Overlay
*overlay
;
367 * Captures video frames from V4L2 source
369 struct VideoCapture
: VideoSource
371 VideoCapture(const char *dev_name
, int width
, int height
, int interlace_tb_swapped
=0, int queue_len
=4);
375 void v_start_capture();
376 void v_stop_capture();
386 vector
<Frame
> buffers
;
394 * Sends video frames to network
396 struct NetTx
: IVideoSink
398 NetTx(const char *dest
, int port
, int mtu
);
402 bool v_query_framebuf(Frame
*f
);
403 void v_on_frame(const Frame
*f
);
406 struct sockaddr_in tx_addr
;
412 * Receives video frames from network
414 struct NetRx
: VideoSource
416 NetRx(const char *listen_on
, int port
, int mtu
);
420 void v_start_capture();
421 void v_stop_capture();
424 void __handle_recv(unsigned len
);
428 struct sockaddr_in rx_addr
;
433 vector
<__u8
> fragbuf
; // XXX align?
434 vector
<__u8
> framebuf
; // XXX align?
436 struct Frame f
; /* current frame */
438 /* current rx state */
439 __u16 nframe
, nfragment
, fragments_total
;
440 __u16 frag_received
; /* # of fragments received in current frame */
441 __u32 frag_dropped_total
; /* total # of dropped fragments */
445 /* XXX is this a good idea ? */
446 struct RawvError
: std::runtime_error
448 RawvError(const char *msg
);
449 ~RawvError() throw();
452 void die(const char *fmt
, ...);
453 void die_errno(const char *msg
);
454 void warn(const char *fmt
, ...);
455 void warn_errno(const char *msg
);