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.5 2007/07/05 06:36:22 rjongbloed
28 * Fixed MSVC compiler warning.
30 * Revision 1.4 2007/07/05 06:25:13 rjongbloed
31 * Fixed GNU compiler warnings.
33 * Revision 1.3 2007/06/30 14:00:05 dsandras
34 * Fixed previous commit so that things at least compile. Untested.
36 * Revision 1.2 2007/06/29 23:24:25 csoutheren
37 * More RFC4175 implementation
39 * Revision 1.1 2007/05/31 14:11:45 csoutheren
40 * Add initial support for RFC 4175 uncompressed video encoding
46 #include <ptclib/random.h>
48 #include <opal/buildopts.h>
52 #include <codec/rfc4175.h>
53 #include <codec/opalplugin.h>
55 namespace PWLibStupidLinkerHacks
{
59 OPAL_REGISTER_RFC4175_VIDEO(RGB24
)
60 OPAL_REGISTER_RFC4175_VIDEO(YUV420P
)
62 #define FRAME_WIDTH 1920
63 #define FRAME_HEIGHT 1080
66 #define REASONABLE_UDP_PACKET_SIZE 1000
68 const OpalVideoFormat
& GetOpalRFC4175_YUV420P()
70 static const OpalVideoFormat
RFC4175YUV420P(
72 RTP_DataFrame::MaxPayloadType
,
74 FRAME_WIDTH
, FRAME_HEIGHT
,
76 0xffffffff //12*FRAME_WIDTH*FRAME_HEIGHT*FRAME_RATE // Bandwidth
78 return RFC4175YUV420P
;
81 const OpalVideoFormat
& GetOpalRFC4175_RGB24()
83 static const OpalVideoFormat
RFC4175RGB24(
85 RTP_DataFrame::MaxPayloadType
,
89 0xffffffff //24*FRAME_WIDTH*FRAME_HEIGHT*FRAME_RATE // Bandwidth
94 /////////////////////////////////////////////////////////////////////////////
96 OpalRFC4175Transcoder::OpalRFC4175Transcoder(
97 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
98 const OpalMediaFormat
& outputMediaFormat
///< Output media format
100 : OpalVideoTranscoder(inputMediaFormat
, outputMediaFormat
)
104 PINDEX
OpalRFC4175Transcoder::RFC4175HeaderSize(PINDEX lines
)
105 { return 2 + lines
*6; }
107 PINDEX
OpalRFC4175Transcoder::GetOptimalDataFrameSize(BOOL
/*input*/) const
112 /////////////////////////////////////////////////////////////////////////////
114 OpalRFC4175Encoder::OpalRFC4175Encoder(
115 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
116 const OpalMediaFormat
& outputMediaFormat
///< Output media format
117 ) : OpalRFC4175Transcoder(inputMediaFormat
, outputMediaFormat
)
119 extendedSequenceNumber
= PRandom::Number();
122 BOOL
OpalRFC4175Encoder::ConvertFrames(const RTP_DataFrame
& input
, RTP_DataFrameList
& output
)
124 // make sure the incoming frame is big enough for a frame header
125 if (input
.GetPayloadSize() < (int)(sizeof(PluginCodec_Video_FrameHeader
))) {
126 PTRACE(1,"RFC4175\tPayload of grabbed frame too small for frame header");
130 PluginCodec_Video_FrameHeader
* header
= (PluginCodec_Video_FrameHeader
*)input
.GetPayloadPtr();
131 if (header
->x
!= 0 && header
->y
!= 0) {
132 PTRACE(1,"RFC4175\tVideo grab of partial frame unsupported");
136 // get information from frame header
137 PINDEX frameHeight
= header
->height
;
138 PINDEX frameWidth
= header
->width
;
139 PINDEX frameWidthInBytes
= PixelsToBytes(frameWidth
);
141 // make sure the incoming frame is big enough for the specified frame size
142 if (input
.GetPayloadSize() < (int)(sizeof(PluginCodec_Video_FrameHeader
) + frameHeight
*frameWidthInBytes
)) {
143 PTRACE(1,"RFC4175\tPayload of grabbed frame too small for full frame");
147 // calculate how many scan lines will fit in a reasonable UDP packet
148 PINDEX linesPerPacket
= REASONABLE_UDP_PACKET_SIZE
/ frameWidthInBytes
;
150 // if a scan line is longer than a reasonable packet, then return error for now
151 if (linesPerPacket
<= 0) {
152 PTRACE(1,"RFC4175\tframe width too large");
156 // encode the scan lines
158 while (y
< frameHeight
) {
160 // allocate a new output frame
161 RTP_DataFrame
* frame
= new RTP_DataFrame
;
162 output
.Append(frame
);
164 // populate RTP fields
165 frame
->SetTimestamp(input
.GetTimestamp());
166 frame
->SetSequenceNumber((WORD
)(extendedSequenceNumber
& 0xffff));
168 // calculate number of scanlines in this packet
169 PINDEX lineCount
= PMIN(linesPerPacket
, frameHeight
-y
);
171 // set size of the packet
172 frame
->SetPayloadSize(RFC4175HeaderSize(lineCount
) + lineCount
*frameWidthInBytes
);
174 // populate extended sequence number
175 *(PUInt16b
*)frame
->GetPayloadPtr() = (WORD
)(extendedSequenceNumber
>> 16);
177 // initialise scan table
178 BYTE
* ptr
= frame
->GetPayloadPtr() + 2;
181 for (j
= 0; j
< lineCount
; ++j
) {
184 *(PUInt16b
*)ptr
= (WORD
)(PixelsToBytes(frameWidth
));
187 // line number + field flag
188 *(PUInt16b
*)ptr
= (WORD
)((y
+j
) & 0x7fff);
191 // pixel offset of scanline start
192 *(PUInt16b
*)ptr
= (WORD
)PixelsToBytes(offset
) & ((j
== (lineCount
-1)) ? 0x8000 : 0x0000);
195 // move to next scan line
196 offset
+= frameWidth
;
199 // copy scan line data
200 memcpy(ptr
, OPAL_VIDEO_FRAME_DATA_PTR(header
)+y
*frameWidthInBytes
, lineCount
*frameWidthInBytes
);
202 // move to next block of scan lines
205 // increment sequence number
206 ++extendedSequenceNumber
;
209 // set marker bit in last packet, if any packets created
210 if (output
.GetSize() > 0)
211 output
[output
.GetSize()-1].SetMarker(TRUE
);
216 /////////////////////////////////////////////////////////////////////////////
218 OpalRFC4175Decoder::OpalRFC4175Decoder(
219 const OpalMediaFormat
& inputMediaFormat
, ///< Input media format
220 const OpalMediaFormat
& outputMediaFormat
///< Output media format
221 ) : OpalRFC4175Transcoder(inputMediaFormat
, outputMediaFormat
)
226 BOOL
OpalRFC4175Decoder::ConvertFrames(const RTP_DataFrame
& input
, RTP_DataFrameList
& output
)
228 if (input
.GetPayloadSize() < 8) {
229 PTRACE(1,"RFC4175\tinput frame too small for header");
233 // get pointer to scanline table
234 BYTE
* ptr
= input
.GetPayloadPtr() + 2;
236 BOOL lastLine
= FALSE
;
237 PINDEX firstLineLength
= 0;
238 BOOL firstLine
= TRUE
;
239 PINDEX lineCount
= 0;
240 PINDEX maxLineNumber
= 0;
244 // ensure there is enough payload for this header
245 if ((2 + ((lineCount
+1)*6)) >= input
.GetPayloadSize()) {
246 PTRACE(1,"RFC4175\tinput frame too small for scan line table");
251 PINDEX lineLength
= BytesToPixels(*(PUInt16b
*)ptr
);
255 WORD lineNumber
= ((*(PUInt16b
*)ptr
) & 0x7fff);
258 // pixel offset of scanline start
259 WORD offset
= *(PUInt16b
*)ptr
;
262 // detect if last scanline in table
263 if (offset
& 0x8000) {
268 // we don't handle partial lines or variable length lines
270 PTRACE(1,"RFC4175\tpartial lines not supported");
272 } else if (firstLine
) {
273 firstLineLength
= lineLength
;
275 } else if (lineLength
!= firstLineLength
) {
276 PTRACE(1,"RFC4175\tline length changed during frame");
280 // keep track of max line number
281 if (lineNumber
> maxLineNumber
)
282 maxLineNumber
= lineNumber
;
289 // if this is the first frame, allocate the destination frame
297 BOOL
OpalRFC4175Decoder::Initialise()
305 #endif // OPAL_RFC4175