4 * RFC4175 transport for uncompressed video
6 * Open Phone Abstraction Library
8 * Copyright (C) 2007 Post Increment
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Open Phone Abstraction Library.
22 * The Initial Developer of the Original Code is Post Increment
24 * Contributor(s): ______________________________________.
27 * Revision 1.8 2007/09/09 23:44:15 rjongbloed
28 * Fixed payload type and encoding name
30 * Revision 1.7 2007/08/29 00:46:13 csoutheren
31 * Change base class for RFC4175 transcoder
33 * Revision 1.6 2007/08/03 07:21:02 csoutheren
36 * Revision 1.5 2007/07/05 06:36:22 rjongbloed
37 * Fixed MSVC compiler warning.
39 * Revision 1.4 2007/07/05 06:25:13 rjongbloed
40 * Fixed GNU compiler warnings.
42 * Revision 1.3 2007/06/30 14:00:05 dsandras
43 * Fixed previous commit so that things at least compile. Untested.
45 * Revision 1.2 2007/06/29 23:24:25 csoutheren
46 * More RFC4175 implementation
48 * Revision 1.1 2007/05/31 14:11:45 csoutheren
49 * Add initial support for RFC 4175 uncompressed video encoding
55 #include <ptclib/random.h>
57 #include <opal/buildopts.h>
61 #include <codec/rfc4175.h>
62 #include <codec/opalplugin.h>
64 namespace PWLibStupidLinkerHacks
{
68 OPAL_REGISTER_RFC4175_VIDEO(RGB24
)
69 OPAL_REGISTER_RFC4175_VIDEO(YUV420P
)
71 #define FRAME_WIDTH 1920
72 #define FRAME_HEIGHT 1080
75 #define REASONABLE_UDP_PACKET_SIZE 1000
77 const OpalVideoFormat
& GetOpalRFC4175_YUV420P()
79 static const OpalVideoFormat
RFC4175YUV420P(
81 RTP_DataFrame::DynamicBase
,
83 FRAME_WIDTH
, FRAME_HEIGHT
,
85 0xffffffff //12*FRAME_WIDTH*FRAME_HEIGHT*FRAME_RATE // Bandwidth
87 return RFC4175YUV420P
;
90 const OpalVideoFormat
& GetOpalRFC4175_RGB24()
92 static const OpalVideoFormat
RFC4175RGB24(
94 RTP_DataFrame::DynamicBase
,
98 0xffffffff //24*FRAME_WIDTH*FRAME_HEIGHT*FRAME_RATE // Bandwidth
103 /////////////////////////////////////////////////////////////////////////////
105 OpalRFC4175Transcoder::OpalRFC4175Transcoder(
106 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
107 const OpalMediaFormat
& outputMediaFormat
///< Output media format
109 : OpalUncompVideoTranscoder(inputMediaFormat
, outputMediaFormat
)
113 PINDEX
OpalRFC4175Transcoder::RFC4175HeaderSize(PINDEX lines
)
114 { return 2 + lines
*6; }
116 /////////////////////////////////////////////////////////////////////////////
118 OpalRFC4175Encoder::OpalRFC4175Encoder(
119 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
120 const OpalMediaFormat
& outputMediaFormat
///< Output media format
121 ) : OpalRFC4175Transcoder(inputMediaFormat
, outputMediaFormat
)
123 extendedSequenceNumber
= PRandom::Number();
126 BOOL
OpalRFC4175Encoder::ConvertFrames(const RTP_DataFrame
& input
, RTP_DataFrameList
& output
)
128 // make sure the incoming frame is big enough for a frame header
129 if (input
.GetPayloadSize() < (int)(sizeof(PluginCodec_Video_FrameHeader
))) {
130 PTRACE(1,"RFC4175\tPayload of grabbed frame too small for frame header");
134 PluginCodec_Video_FrameHeader
* header
= (PluginCodec_Video_FrameHeader
*)input
.GetPayloadPtr();
135 if (header
->x
!= 0 && header
->y
!= 0) {
136 PTRACE(1,"RFC4175\tVideo grab of partial frame unsupported");
140 // get information from frame header
141 PINDEX frameHeight
= header
->height
;
142 PINDEX frameWidth
= header
->width
;
143 PINDEX frameWidthInBytes
= PixelsToBytes(frameWidth
);
145 // make sure the incoming frame is big enough for the specified frame size
146 if (input
.GetPayloadSize() < (int)(sizeof(PluginCodec_Video_FrameHeader
) + frameHeight
*frameWidthInBytes
)) {
147 PTRACE(1,"RFC4175\tPayload of grabbed frame too small for full frame");
151 // calculate how many scan lines will fit in a reasonable UDP packet
152 PINDEX linesPerPacket
= REASONABLE_UDP_PACKET_SIZE
/ frameWidthInBytes
;
154 // if a scan line is longer than a reasonable packet, then return error for now
155 if (linesPerPacket
<= 0) {
156 PTRACE(1,"RFC4175\tframe width too large");
160 // encode the scan lines
162 while (y
< frameHeight
) {
164 // allocate a new output frame
165 RTP_DataFrame
* frame
= new RTP_DataFrame
;
166 output
.Append(frame
);
168 // populate RTP fields
169 frame
->SetTimestamp(input
.GetTimestamp());
170 frame
->SetSequenceNumber((WORD
)(extendedSequenceNumber
& 0xffff));
172 // calculate number of scanlines in this packet
173 PINDEX lineCount
= PMIN(linesPerPacket
, frameHeight
-y
);
175 // set size of the packet
176 frame
->SetPayloadSize(RFC4175HeaderSize(lineCount
) + lineCount
*frameWidthInBytes
);
178 // populate extended sequence number
179 *(PUInt16b
*)frame
->GetPayloadPtr() = (WORD
)(extendedSequenceNumber
>> 16);
181 // initialise scan table
182 BYTE
* ptr
= frame
->GetPayloadPtr() + 2;
185 for (j
= 0; j
< lineCount
; ++j
) {
188 *(PUInt16b
*)ptr
= (WORD
)(PixelsToBytes(frameWidth
));
191 // line number + field flag
192 *(PUInt16b
*)ptr
= (WORD
)((y
+j
) & 0x7fff);
195 // pixel offset of scanline start
196 *(PUInt16b
*)ptr
= (WORD
)PixelsToBytes(offset
) & ((j
== (lineCount
-1)) ? 0x8000 : 0x0000);
199 // move to next scan line
200 offset
+= frameWidth
;
203 // copy scan line data
204 memcpy(ptr
, OPAL_VIDEO_FRAME_DATA_PTR(header
)+y
*frameWidthInBytes
, lineCount
*frameWidthInBytes
);
206 // move to next block of scan lines
209 // increment sequence number
210 ++extendedSequenceNumber
;
213 // set marker bit in last packet, if any packets created
214 if (output
.GetSize() > 0)
215 output
[output
.GetSize()-1].SetMarker(TRUE
);
220 /////////////////////////////////////////////////////////////////////////////
222 OpalRFC4175Decoder::OpalRFC4175Decoder(
223 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
224 const OpalMediaFormat
& outputMediaFormat
///< Output media format
225 ) : OpalRFC4175Transcoder(inputMediaFormat
, outputMediaFormat
)
230 BOOL
OpalRFC4175Decoder::ConvertFrames(const RTP_DataFrame
& input
, RTP_DataFrameList
& /*output*/)
232 if (input
.GetPayloadSize() < 8) {
233 PTRACE(1,"RFC4175\tinput frame too small for header");
237 // get pointer to scanline table
238 BYTE
* ptr
= input
.GetPayloadPtr() + 2;
240 BOOL lastLine
= FALSE
;
241 PINDEX firstLineLength
= 0;
242 BOOL firstLine
= TRUE
;
243 PINDEX lineCount
= 0;
244 PINDEX maxLineNumber
= 0;
248 // ensure there is enough payload for this header
249 if ((2 + ((lineCount
+1)*6)) >= input
.GetPayloadSize()) {
250 PTRACE(1,"RFC4175\tinput frame too small for scan line table");
255 PINDEX lineLength
= BytesToPixels(*(PUInt16b
*)ptr
);
259 WORD lineNumber
= ((*(PUInt16b
*)ptr
) & 0x7fff);
262 // pixel offset of scanline start
263 WORD offset
= *(PUInt16b
*)ptr
;
266 // detect if last scanline in table
267 if (offset
& 0x8000) {
272 // we don't handle partial lines or variable length lines
274 PTRACE(1,"RFC4175\tpartial lines not supported");
276 } else if (firstLine
) {
277 firstLineLength
= lineLength
;
279 } else if (lineLength
!= firstLineLength
) {
280 PTRACE(1,"RFC4175\tline length changed during frame");
284 // keep track of max line number
285 if (lineNumber
> maxLineNumber
)
286 maxLineNumber
= lineNumber
;
293 // if this is the first frame, allocate the destination frame
300 BOOL
OpalRFC4175Decoder::Initialise()
308 #endif // OPAL_RFC4175