1 /*
2 * H.263 Plugin codec for OpenH323/OPAL
3 *
4 * This code is based on the following files from the OPAL project which
5 * have been removed from the current build and distributions but are still
6 * available in the CVS "attic"
7 *
8 * src/codecs/h263codec.cxx
9 * include/codecs/h263codec.h
10
11 * The original files, and this version of the original code, are released under the same
12 * MPL 1.0 license. Substantial portions of the original code were contributed
13 * by Salyens and March Networks and their right to be identified as copyright holders
14 * of the original code portions and any parts now included in this new copy is asserted through
15 * their inclusion in the copyright notices below.
16 *
17 * Copyright (C) 2007 Matthias Schneider
18 * Copyright (C) 2006 Post Increment
19 * Copyright (C) 2005 Salyens
20 * Copyright (C) 2001 March Networks Corporation
21 * Copyright (C) 1999-2000 Equivalence Pty. Ltd.
22 *
23 * The contents of this file are subject to the Mozilla Public License
24 * Version 1.0 (the "License"); you may not use this file except in
25 * compliance with the License. You may obtain a copy of the License at
26 * http://www.mozilla.org/MPL/
27 *
28 * Software distributed under the License is distributed on an "AS IS"
29 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
30 * the License for the specific language governing rights and limitations
31 * under the License.
32 *
33 * The Original Code is Open H323 Library.
34 *
35 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
36 *
37 * Contributor(s): Matthias Schneider (ma30002000@yahoo.de)
38 * Guilhem Tardy (gtardy@salyens.com)
39 * Craig Southeren (craigs@postincrement.com)
40 *
41 * $Revision: 28438 $
42 * $Author: rjongbloed $
43 * $Date: 2012-10-02 00:34:04 -0500 (Tue, 02 Oct 2012) $
44 */
45
46 #ifndef PLUGIN_CODEC_DLL_EXPORTS
47 #include "plugin-config.h"
48 #endif
49
50 #include "h263-1998.h"
51 extern "C"
52 {
53 #include <libavutil/opt.h>
54 }
55 #include <limits>
56 #include <iomanip>
57 #include <stdio.h>
58 #include <math.h>
59 #include <string.h>
60
61 #include "../common/mpi.h"
62 #include "../common/dyna.h"
63 #include "../common/ffmpeg.h"
64 #include "rfc2190.h"
65 #include "rfc2429.h"
66
67
68 static const char YUV420PDesc[] = { "YUV420P" };
69 static const char h263PDesc[] = { "H.263plus" };
70 static const char sdpH263P[] = { "H263-1998" };
71
72 static const char h263Desc[] = { "H.263" };
73 static const char sdpH263[] = { "H263" };
74
75
76 #define MAX_H263_CUSTOM_SIZES 10
77 #define DEFAULT_CUSTOM_MPI "0,0," STRINGIZE(PLUGINCODEC_MPI_DISABLED)
78
79 static struct StdSizes {
80 enum {
81 SQCIF,
82 QCIF,
83 CIF,
84 CIF4,
85 CIF16,
86 NumStdSizes,
87 UnknownStdSize = NumStdSizes
88 };
89
90 int width;
91 int height;
92 const char * optionName;
93 } StandardVideoSizes[StdSizes::NumStdSizes] = {
94 { SQCIF_WIDTH, SQCIF_HEIGHT, PLUGINCODEC_SQCIF_MPI },
95 { QCIF_WIDTH, QCIF_HEIGHT, PLUGINCODEC_QCIF_MPI },
96 { CIF_WIDTH, CIF_HEIGHT, PLUGINCODEC_CIF_MPI },
97 { CIF4_WIDTH, CIF4_HEIGHT, PLUGINCODEC_CIF4_MPI },
98 { CIF16_WIDTH, CIF16_HEIGHT, PLUGINCODEC_CIF16_MPI },
99 };
100
101 static FFMPEGLibrary FFMPEGLibraryInstance(AV_CODEC_ID_H263P);
102
103
104 /////////////////////////////////////////////////////////////////////////////
105
num2str(int num)106 static char * num2str(int num)
107 {
108 char buf[20];
109 sprintf(buf, "%i", num);
110 return strdup(buf);
111 }
112
113 #if TRACE_RTP
114
DumpRTPPayload(std::ostream & trace,const RTPFrame & rtp,unsigned max)115 static void DumpRTPPayload(std::ostream & trace, const RTPFrame & rtp, unsigned max)
116 {
117 if (max > rtp.GetPayloadSize())
118 max = rtp.GetPayloadSize();
119 BYTE * ptr = rtp.GetPayloadPtr();
120 trace << std::hex << std::setfill('0') << std::setprecision(2);
121 while (max-- > 0)
122 trace << (int) *ptr++ << ' ';
123 trace << std::setfill(' ') << std::dec;
124 }
125
RTPDump(std::ostream & trace,const RTPFrame & rtp)126 static void RTPDump(std::ostream & trace, const RTPFrame & rtp)
127 {
128 trace << "seq=" << rtp.GetSequenceNumber()
129 << ",ts=" << rtp.GetTimestamp()
130 << ",mkr=" << rtp.GetMarker()
131 << ",pt=" << (int)rtp.GetPayloadType()
132 << ",ps=" << rtp.GetPayloadSize();
133 }
134
RFC2190Dump(std::ostream & trace,const RTPFrame & rtp)135 static void RFC2190Dump(std::ostream & trace, const RTPFrame & rtp)
136 {
137 RTPDump(trace, rtp);
138 if (rtp.GetPayloadSize() > 2) {
139 bool iFrame = false;
140 char mode;
141 BYTE * payload = rtp.GetPayloadPtr();
142 if ((payload[0] & 0x80) == 0) {
143 mode = 'A';
144 iFrame = (payload[1] & 0x10) == 0;
145 }
146 else if ((payload[0] & 0x40) == 0) {
147 mode = 'B';
148 iFrame = (payload[4] & 0x80) == 0;
149 }
150 else {
151 mode = 'C';
152 iFrame = (payload[4] & 0x80) == 0;
153 }
154 trace << "mode=" << mode << ",I=" << (iFrame ? "yes" : "no");
155 }
156 trace << ",data=";
157 DumpRTPPayload(trace, rtp, 10);
158 }
159
RFC2429Dump(std::ostream & trace,const RTPFrame & rtp)160 static void RFC2429Dump(std::ostream & trace, const RTPFrame & rtp)
161 {
162 RTPDump(trace, rtp);
163 trace << ",data=";
164 DumpRTPPayload(trace, rtp, 10);
165 }
166
167 #define CODEC_TRACER_RTP(text, rtp, func) \
168 if (PTRACE_CHECK(6)) { \
169 std::ostringstream strm; strm << text; func(strm, rtp); \
170 PluginCodec_LogFunctionInstance(6, __FILE__, __LINE__, m_prefix, strm.str().c_str()); \
171 } else (void)0
172
173 #else
174
175 #define CODEC_TRACER_RTP(text, rtp, func)
176
177 #endif
178
179 /////////////////////////////////////////////////////////////////////////////
180
H263_Base_EncoderContext(const char * prefix,Packetizer * packetizer)181 H263_Base_EncoderContext::H263_Base_EncoderContext(const char * prefix, Packetizer * packetizer)
182 : m_prefix(prefix)
183 , m_codec(NULL)
184 , m_context(NULL)
185 , m_inputFrame(NULL)
186 , m_alignedInputYUV(NULL)
187 , m_packetizer(packetizer)
188 {
189 FFMPEGLibraryInstance.Load();
190 }
191
~H263_Base_EncoderContext()192 H263_Base_EncoderContext::~H263_Base_EncoderContext()
193 {
194 WaitAndSignal m(m_mutex);
195
196 CloseCodec();
197
198 if (m_context != NULL)
199 FFMPEGLibraryInstance.AvcodecFree(m_context);
200 if (m_inputFrame != NULL)
201 FFMPEGLibraryInstance.AvcodecFree(m_inputFrame);
202 if (m_alignedInputYUV != NULL)
203 free(m_alignedInputYUV);
204
205 delete m_packetizer;
206
207 PTRACE(4, m_prefix, "Encoder closed");
208 }
209
Init(AVCodecID codecId)210 bool H263_Base_EncoderContext::Init(AVCodecID codecId)
211 {
212 PTRACE(5, m_prefix, "Opening encoder");
213
214 if (!FFMPEGLibraryInstance.IsLoaded())
215 return false;
216
217 m_codec = FFMPEGLibraryInstance.AvcodecFindEncoder(codecId);
218 if (m_codec == NULL) {
219 PTRACE(1, m_prefix, "Codec not found for encoder");
220 return false;
221 }
222
223 m_context = FFMPEGLibraryInstance.AvcodecAllocContext();
224 if (m_context == NULL) {
225 PTRACE(1, m_prefix, "Failed to allocate context for encoder");
226 return false;
227 }
228
229 m_inputFrame = FFMPEGLibraryInstance.AvcodecAllocFrame();
230 if (m_inputFrame == NULL) {
231 PTRACE(1, m_prefix, "Failed to allocate frame for encoder");
232 return false;
233 }
234
235 m_context->opaque = this;
236
237 m_context->flags = AV_CODEC_FLAG_TRUNCATED // Possible missing packets
238 ;
239
240 m_context->pix_fmt = AV_PIX_FMT_YUV420P;
241 m_context->gop_size = H263_KEY_FRAME_INTERVAL;
242
243 // X-Lite does not like Custom Picture frequency clocks... stick to 29.97Hz
244 m_context->time_base.num = 100;
245 m_context->time_base.den = 2997;
246
247 // debugging flags
248 #if PLUGINCODEC_TRACING
249 if (PTRACE_CHECK(4))
250 m_context->debug |= FF_DEBUG_ER;
251 if (PTRACE_CHECK(5))
252 m_context->debug |= FF_DEBUG_PICT_INFO | FF_DEBUG_RC;
253 if (PTRACE_CHECK(6))
254 m_context->debug |= FF_DEBUG_BUGS | FF_DEBUG_BUFFERS;
255 #endif
256
257 PTRACE(4, m_prefix, "Encoder created (SVN $Revision: 28438 $)");
258
259 return true;
260 }
261
SetOptions(const char * const * options)262 bool H263_Base_EncoderContext::SetOptions(const char * const * options)
263 {
264 Lock();
265 CloseCodec();
266
267 // get the "frame width" media format parameter to use as a hint for the encoder to start off
268 for (const char * const * option = options; *option != NULL; option += 2)
269 SetOption(option[0], option[1]);
270
271 bool ok = OpenCodec();
272 Unlock();
273 return ok;
274 }
275
276
SetOption(const char * option,const char * value)277 void H263_Base_EncoderContext::SetOption(const char * option, const char * value)
278 {
279 if (STRCMPI(option, PLUGINCODEC_OPTION_FRAME_TIME) == 0) {
280 m_context->time_base.den = 2997;
281 m_context->time_base.num = atoi(value)*m_context->time_base.den/VIDEO_CLOCKRATE;
282 return;
283 }
284
285 if (STRCMPI(option, PLUGINCODEC_OPTION_FRAME_WIDTH) == 0) {
286 FFMPEGLibraryInstance.AvSetDimensions(m_context, atoi(value), m_context->height);
287 return;
288 }
289
290 if (STRCMPI(option, PLUGINCODEC_OPTION_FRAME_HEIGHT) == 0) {
291 FFMPEGLibraryInstance.AvSetDimensions(m_context, m_context->width, atoi(value));
292 return;
293 }
294
295 if (STRCMPI(option, PLUGINCODEC_OPTION_MAX_TX_PACKET_SIZE) == 0) {
296 m_context->rtp_payload_size = atoi(value);
297 m_packetizer->SetMaxPayloadSize(m_context->rtp_payload_size);
298 return;
299 }
300
301 if (STRCMPI(option, PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0) {
302 m_context->bit_rate = atoi(value);
303 return;
304 }
305
306 if (STRCMPI(option, PLUGINCODEC_OPTION_TEMPORAL_SPATIAL_TRADE_OFF) == 0) {
307 m_context->qmax = atoi(value);
308 if (m_context->qmax <= m_context->qmin)
309 m_context->qmax = m_context->qmin+1;
310 return;
311 }
312
313 if (STRCMPI(option, PLUGINCODEC_OPTION_TX_KEY_FRAME_PERIOD) == 0) {
314 m_context->gop_size = atoi(value);
315 return;
316 }
317
318 if (STRCMPI(option, H263_ANNEX_D) == 0) {
319 // Annex D: Unrestructed Motion Vectors
320 // Level 2+
321 // works with eyeBeam, signaled via non-standard "D"
322 if (atoi(value) == 1)
323 av_opt_set_int(m_context->priv_data, "umv", 1, 0);
324 else
325 av_opt_set_int(m_context->priv_data, "umv", 0, 0);
326 return;
327 }
328
329 #if 0 // DO NOT ENABLE THIS FLAG. FFMPEG IS NOT THREAD_SAFE WHEN THIS FLAG IS SET
330 if (STRCMPI(option, H263_ANNEX_F) == 0) {
331 // Annex F: Advanced Prediction Mode
332 // does not work with eyeBeam
333 if (atoi(value) == 1)
334 av_opt_set_int(m_context->priv_data, "obmc", 1, 0);
335 else
336 av_opt_set_int(m_context->priv_data, "obmc", 0, 0);
337 return;
338 }
339 #endif
340
341 if (STRCMPI(option, H263_ANNEX_I) == 0) {
342 // Annex I: Advanced Intra Coding
343 // Level 3+
344 // works with eyeBeam
345 if (atoi(value) == 1)
346 m_context->flags |= AV_CODEC_FLAG_AC_PRED;
347 else
348 m_context->flags &= ~AV_CODEC_FLAG_AC_PRED;
349 return;
350 }
351
352 if (STRCMPI(option, H263_ANNEX_J) == 0) {
353 // Annex J: Deblocking Filter
354 // works with eyeBeam
355 if (atoi(value) == 1)
356 m_context->flags |= AV_CODEC_FLAG_LOOP_FILTER;
357 else
358 m_context->flags &= ~AV_CODEC_FLAG_LOOP_FILTER;
359 return;
360 }
361
362 if (STRCMPI(option, H263_ANNEX_K) == 0) {
363 // Annex K: Slice Structure
364 // does not work with eyeBeam
365 if (atoi(value) != 0)
366 av_opt_set_int(m_context->priv_data, "structured_slices", 1, 0);
367 else
368 av_opt_set_int(m_context->priv_data, "structured_slices", 0, 0);
369 return;
370 }
371
372 if (STRCMPI(option, H263_ANNEX_S) == 0) {
373 // Annex S: Alternative INTER VLC mode
374 // does not work with eyeBeam
375 if (atoi(value) == 1)
376 av_opt_set_int(m_context->priv_data, "aiv", 1, 0);
377 else
378 av_opt_set_int(m_context->priv_data, "aiv", 0, 0);
379 return;
380 }
381
382 if (STRCMPI(option, PLUGINCODEC_MEDIA_PACKETIZATION) == 0 ||
383 STRCMPI(option, PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
384 if (strstr(value, m_packetizer->GetName()) == NULL) {
385 PTRACE(4, m_prefix, "Packetisation changed to " << value);
386 delete m_packetizer;
387 if (STRCMPI(value, "RFC2429") == 0)
388 m_packetizer = new RFC2429Frame;
389 else
390 m_packetizer = new RFC2190Packetizer;
391 }
392 }
393 }
394
395
OpenCodec()396 bool H263_Base_EncoderContext::OpenCodec()
397 {
398 if (m_codec == NULL) {
399 PTRACE(1, m_prefix, "Codec not initialized");
400 return false;
401 }
402
403 m_context->rc_min_rate = m_context->bit_rate;
404 m_context->rc_max_rate = m_context->bit_rate;
405 m_context->rc_buffer_size = m_context->bit_rate*4; // Enough bits for 4 seconds
406 m_context->bit_rate_tolerance = m_context->bit_rate/10;
407
408 // FFMPEG requires bit rate tolerance to be at least one frame size
409 int oneFrameBits = (int)((int64_t)m_context->bit_rate*m_context->time_base.num/m_context->time_base.den) + 1;
410 if (m_context->bit_rate_tolerance < oneFrameBits) {
411 PTRACE(4, m_prefix, "Limited bit_rate_tolerance "
412 "(" << m_context->bit_rate_tolerance << ") to size of one frame (" << oneFrameBits << ')');
413 m_context->bit_rate_tolerance = oneFrameBits;
414 }
415
416 m_context->i_quant_factor = (float)-0.6; // qscale factor between p and i frames
417 m_context->i_quant_offset = (float)0.0; // qscale offset between p and i frames
418 m_context->max_qdiff = 10; // was 3 // max q difference between frames
419 m_context->qcompress = 0.5; // qscale factor between easy & hard scenes (0.0-1.0)
420
421 // Lagrange multipliers - this is how the context defaults do it:
422 av_opt_set_int(m_context->priv_data, "lmin", m_context->qmin * FF_QP2LAMBDA, 0);
423 av_opt_set_int(m_context->priv_data, "lmax", m_context->qmax * FF_QP2LAMBDA, 0);
424
425 // YUV420P input
426 m_inputFrame->linesize[0] = m_context->width;
427 m_inputFrame->linesize[1] = m_context->width / 2;
428 m_inputFrame->linesize[2] = m_context->width / 2;
429
430 if (m_alignedInputYUV != NULL)
431 free(m_alignedInputYUV);
432
433 size_t planeSize = m_context->width*m_context->height;
434 m_alignedInputYUV = (uint8_t *)malloc(planeSize*3/2+16);
435
436 m_inputFrame->data[0] = m_alignedInputYUV + 16 - (((size_t)m_alignedInputYUV) & 0xf);
437 m_inputFrame->data[1] = m_inputFrame->data[0] + planeSize;
438 m_inputFrame->data[2] = m_inputFrame->data[1] + (planeSize / 4);
439
440 // Dump info
441 PTRACE(5, m_prefix, "Size is " << m_context->width << "x" << m_context->height);
442 PTRACE(5, m_prefix, "GOP is " << m_context->gop_size);
443 PTRACE(5, m_prefix, "time_base set to " << m_context->time_base.num << '/' << m_context->time_base.den
444 << " (" << std::setprecision(2) << ((double)m_context->time_base.den/m_context->time_base.num) << "fps)");
445 PTRACE(5, m_prefix, "bit_rate set to " << m_context->bit_rate);
446 PTRACE(5, m_prefix, "rc_max_rate is " << m_context->rc_max_rate);
447 PTRACE(5, m_prefix, "rc_min_rate set to " << m_context->rc_min_rate);
448 PTRACE(5, m_prefix, "bit_rate_tolerance set to " <<m_context->bit_rate_tolerance);
449 PTRACE(5, m_prefix, "qmin set to " << m_context->qmin);
450 PTRACE(5, m_prefix, "qmax set to " << m_context->qmax);
451 PTRACE(5, m_prefix, "payload size set to " << m_context->rtp_payload_size);
452
453 return FFMPEGLibraryInstance.AvcodecOpen(m_context, m_codec, NULL) == 0;
454 }
455
CloseCodec()456 void H263_Base_EncoderContext::CloseCodec()
457 {
458 if (m_context != NULL && m_context->codec != NULL)
459 FFMPEGLibraryInstance.AvcodecClose(m_context);
460 }
461
Lock()462 void H263_Base_EncoderContext::Lock()
463 {
464 m_mutex.Wait();
465 }
466
Unlock()467 void H263_Base_EncoderContext::Unlock()
468 {
469 m_mutex.Signal();
470 }
471
472
EncodeFrames(const BYTE * src,unsigned & srcLen,BYTE * dst,unsigned & dstLen,unsigned int & flags)473 bool H263_Base_EncoderContext::EncodeFrames(const BYTE * src, unsigned & srcLen, BYTE * dst, unsigned & dstLen, unsigned int & flags)
474 {
475 WaitAndSignal m(m_mutex);
476
477 if (m_codec == NULL) {
478 PTRACE(1, m_prefix, "Encoder did not open");
479 return false;
480 }
481
482 // create RTP frame from source buffer
483 RTPFrame srcRTP(src, srcLen);
484
485 // create RTP frame from destination buffer
486 RTPFrame dstRTP(dst, dstLen);
487 dstLen = 0;
488
489 // if still running out packets from previous frame, then return it
490 if (!m_packetizer->GetPacket(dstRTP, flags)) {
491 PluginCodec_Video_FrameHeader * header = (PluginCodec_Video_FrameHeader *)srcRTP.GetPayloadPtr();
492 if (header->x != 0 || header->y != 0) {
493 PTRACE(2, m_prefix, "Video grab of partial frame unsupported, closing down video transmission thread.");
494 return 0;
495 }
496
497 // if this is the first frame, or the frame size has changed, deal wth it
498 if (m_context->width != (int)header->width || m_context->height != (int)header->height) {
499 PTRACE(3, m_prefix, "Resolution has changed - reopening codec");
500 CloseCodec();
501 FFMPEGLibraryInstance.AvSetDimensions(m_context, header->width, header->height);
502 if (!OpenCodec()) {
503 PTRACE(1, m_prefix, "Reopening codec failed");
504 return false;
505 }
506 }
507
508 if (!m_packetizer->Reset(header->width, header->height)) {
509 PTRACE(1, m_prefix, "Unable to allocate memory for packet buffer");
510 return 0;
511 }
512
513 // Need to copy to local buffer to guarantee 16 byte alignment
514 memcpy(m_inputFrame->data[0], OPAL_VIDEO_FRAME_DATA_PTR(header), header->width*header->height*3/2);
515 m_inputFrame->pict_type = (flags & PluginCodec_CoderForceIFrame) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_NONE;
516
517 /*
518 m_inputFrame->pts = (int64_t)srcRTP.GetTimestamp()*m_context->time_base.den/m_context->time_base.num/VIDEO_CLOCKRATE;
519
520 It would be preferable to use the above line which would get the correct bit rate if
521 the grabber is actually slower that the desired frame rate. But due to rounding
522 errors, this could end up with two consecutive frames with the same pts and FFMPEG
523 then spits the dummy and returns -1 error, which kills the stream. Sigh.
524
525 So, we just assume the frame rate is actually correct and use it for bit rate control.
526 */
527 m_inputFrame->pts = AV_NOPTS_VALUE;
528
529 int encodedLen = FFMPEGLibraryInstance.AvcodecEncodeVideo(m_context,
530 m_packetizer->GetBuffer(),
531 m_packetizer->GetMaxSize(),
532 m_inputFrame);
533 if (encodedLen < 0) {
534 PTRACE(1, m_prefix, "Encoder failed");
535 return false;
536 }
537
538 if (encodedLen == 0) {
539 PTRACE(3, m_prefix, "Encoder returned no data");
540 flags |= PluginCodec_ReturnCoderLastFrame;
541 dstLen = 0;
542 return true;
543 }
544
545 // push the encoded frame through the m_packetizer
546 if (!m_packetizer->SetLength(encodedLen)) {
547 PTRACE(1, m_prefix, "Packetizer failed");
548 return false;
549 }
550
551 // return the first encoded block of data
552 if (!m_packetizer->GetPacket(dstRTP, flags)) {
553 PTRACE(3, m_prefix, "No packets encoded for frame");
554 flags |= PluginCodec_ReturnCoderLastFrame;
555 dstLen = 0;
556 return true;
557 }
558 }
559
560 dstRTP.SetTimestamp(srcRTP.GetTimestamp());
561
562 CODEC_TRACER_RTP("Tx frame:", dstRTP, RFC2190Dump);
563 dstLen = dstRTP.GetHeaderSize() + dstRTP.GetPayloadSize();
564
565 return true;
566 }
567
568
569 /////////////////////////////////////////////////////////////////////////////
570
H263_RFC2190_EncoderContext()571 H263_RFC2190_EncoderContext::H263_RFC2190_EncoderContext()
572 : H263_Base_EncoderContext("H.263-RFC2190", new RFC2190Packetizer())
573 {
574 }
575
576
577 //s->avctx->rtp_callback(s->avctx, s->ptr_lastgob, current_packet_size, number_mb)
RTPCallBack(AVCodecContext * avctx,void * data,int size,int mb_nb)578 void H263_RFC2190_EncoderContext::RTPCallBack(AVCodecContext *avctx, void * data, int size, int mb_nb)
579 {
580 ((RFC2190Packetizer *)((H263_RFC2190_EncoderContext *)avctx->opaque)->m_packetizer)->RTPCallBack(data, size, mb_nb);
581 }
582
583
Init()584 bool H263_RFC2190_EncoderContext::Init()
585 {
586 if (!H263_Base_EncoderContext::Init(AV_CODEC_ID_H263))
587 return false;
588
589 #if LIBAVCODEC_RTP_MODE
590 m_context->rtp_mode = 1;
591 #endif
592
593 m_context->rtp_payload_size = PluginCodec_RTP_MaxPayloadSize;
594 m_context->rtp_callback = &H263_RFC2190_EncoderContext::RTPCallBack;
595 m_context->opaque = this; // used to separate out packets from different encode threads
596
597 av_opt_set_int(m_context->priv_data, "umv", 0, 0);
598 m_context->flags &= ~AV_CODEC_FLAG_4MV;
599 #if LIBAVCODEC_RTP_MODE
600 m_context->flags &= ~CODEC_FLAG_H263P_AIC;
601 #endif
602 av_opt_set_int(m_context->priv_data, "aiv", 0, 0);
603 av_opt_set_int(m_context->priv_data, "structured_slices", 0, 0);
604
605 return true;
606 }
607
608
609 /////////////////////////////////////////////////////////////////////////////
610
H263_RFC2429_EncoderContext()611 H263_RFC2429_EncoderContext::H263_RFC2429_EncoderContext()
612 : H263_Base_EncoderContext("H.263-RFC2429", new RFC2429Frame)
613 {
614 }
615
~H263_RFC2429_EncoderContext()616 H263_RFC2429_EncoderContext::~H263_RFC2429_EncoderContext()
617 {
618 }
619
620
Init()621 bool H263_RFC2429_EncoderContext::Init()
622 {
623 return H263_Base_EncoderContext::Init(AV_CODEC_ID_H263P);
624 }
625
626
627 /////////////////////////////////////////////////////////////////////////////
628
Packetizer()629 Packetizer::Packetizer()
630 : m_maxPayloadSize(PluginCodec_RTP_MaxPayloadSize)
631 {
632 }
633
634
635 /////////////////////////////////////////////////////////////////////////////
636
H263_Base_DecoderContext(const char * prefix,Depacketizer * depacketizer)637 H263_Base_DecoderContext::H263_Base_DecoderContext(const char * prefix, Depacketizer * depacketizer)
638 : m_prefix(prefix)
639 , m_codec(NULL)
640 , m_context(NULL)
641 , m_outputFrame(NULL)
642 , m_depacketizer(depacketizer)
643 {
644 if (!FFMPEGLibraryInstance.Load())
645 return;
646
647 if ((m_codec = FFMPEGLibraryInstance.AvcodecFindDecoder(AV_CODEC_ID_H263)) == NULL) {
648 PTRACE(1, m_prefix, "Codec not found for decoder");
649 return;
650 }
651
652 m_context = FFMPEGLibraryInstance.AvcodecAllocContext();
653 if (m_context == NULL) {
654 PTRACE(1, m_prefix, "Failed to allocate context for decoder");
655 return;
656 }
657
658 m_outputFrame = FFMPEGLibraryInstance.AvcodecAllocFrame();
659 if (m_outputFrame == NULL) {
660 PTRACE(1, m_prefix, "Failed to allocate frame for decoder");
661 return;
662 }
663
664 // debugging flags
665 #if PLUGINCODEC_TRACING
666 if (PTRACE_CHECK(4))
667 m_context->debug |= FF_DEBUG_ER;
668 if (PTRACE_CHECK(5))
669 m_context->debug |= FF_DEBUG_PICT_INFO;
670 if (PTRACE_CHECK(6))
671 m_context->debug |= FF_DEBUG_BUGS | FF_DEBUG_BUFFERS;
672 #endif
673
674 m_depacketizer->NewFrame();
675
676 PTRACE(4, m_prefix, "Decoder created (SVN $Revision: 28438 $)");
677 }
678
~H263_Base_DecoderContext()679 H263_Base_DecoderContext::~H263_Base_DecoderContext()
680 {
681 CloseCodec();
682
683 if (m_context != NULL)
684 FFMPEGLibraryInstance.AvcodecFree(m_context);
685 if (m_outputFrame != NULL)
686 FFMPEGLibraryInstance.AvcodecFree(m_outputFrame);
687
688 delete m_depacketizer;
689 }
690
OpenCodec()691 bool H263_Base_DecoderContext::OpenCodec()
692 {
693 if (m_codec == NULL || m_context == NULL || m_outputFrame == NULL) {
694 PTRACE(1, m_prefix, "Codec not initialized");
695 return 0;
696 }
697
698 if (FFMPEGLibraryInstance.AvcodecOpen(m_context, m_codec, NULL) < 0) {
699 PTRACE(1, m_prefix, "Failed to open H.263 decoder");
700 return false;
701 }
702
703 PTRACE(4, m_prefix, "Codec opened");
704
705 return true;
706 }
707
CloseCodec()708 void H263_Base_DecoderContext::CloseCodec()
709 {
710 if (m_context != NULL) {
711 if (m_context->codec != NULL) {
712 FFMPEGLibraryInstance.AvcodecClose(m_context);
713 PTRACE(4, m_prefix, "Closed decoder" );
714 }
715 }
716 }
717
SetOptions(const char * const * options)718 bool H263_Base_DecoderContext::SetOptions(const char * const * options)
719 {
720 for (const char * const * option = options; *option != NULL; option += 2) {
721 if (STRCMPI(option[0], PLUGINCODEC_MEDIA_PACKETIZATION) == 0 ||
722 STRCMPI(option[0], PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
723 if (strstr(option[1], m_depacketizer->GetName()) == NULL) {
724 PTRACE(4, m_prefix, "Packetisation changed to " << option[1]);
725 delete m_depacketizer;
726 if (STRCMPI(option[1], "RFC2429") == 0)
727 m_depacketizer = new RFC2429Frame;
728 else
729 m_depacketizer = new RFC2190Depacketizer;
730 }
731 }
732 }
733 return true;
734 }
735
DecodeFrames(const BYTE * src,unsigned & srcLen,BYTE * dst,unsigned & dstLen,unsigned int & flags)736 bool H263_Base_DecoderContext::DecodeFrames(const BYTE * src, unsigned & srcLen, BYTE * dst, unsigned & dstLen, unsigned int & flags)
737 {
738 // create RTP frame from source buffer
739 RTPFrame srcRTP(src, srcLen);
740
741 CODEC_TRACER_RTP("Tx frame:", srcRTP, DumpPacket);
742
743 // create RTP frame from destination buffer
744 RTPFrame dstRTP(dst, dstLen, 0);
745 dstLen = 0;
746
747 if (!m_depacketizer->AddPacket(srcRTP)) {
748 m_depacketizer->NewFrame();
749 flags = PluginCodec_ReturnCoderRequestIFrame;
750 return true;
751 }
752
753 if (!srcRTP.GetMarker())
754 return true;
755
756 if (!m_depacketizer->IsValid()) {
757 m_depacketizer->NewFrame();
758 PTRACE(4, m_prefix, "Got an invalid frame - skipping");
759 flags = PluginCodec_ReturnCoderRequestIFrame;
760 return true;
761 }
762
763 if (m_depacketizer->IsIntraFrame())
764 flags |= PluginCodec_ReturnCoderIFrame;
765
766 #ifdef FFMPEG_HAS_DECODE_ERROR_COUNT
767 int error_before = m_context->decode_error_count;
768 #endif
769
770 PTRACE(5, m_prefix, "Decoding " << m_depacketizer->GetLength() << " bytes");
771 int gotPicture = 0;
772 int bytesDecoded = FFMPEGLibraryInstance.AvcodecDecodeVideo(m_context,
773 m_outputFrame,
774 &gotPicture,
775 m_depacketizer->GetBuffer(),
776 m_depacketizer->GetLength());
777
778 m_depacketizer->NewFrame();
779
780 #ifdef FFMPEG_HAS_DECODE_ERROR_COUNT
781 // if error occurred, tell the other end to send another I-frame and hopefully we can resync
782 if (error_before != m_context->decode_error_count)
783 flags = PluginCodec_ReturnCoderRequestIFrame;
784 #endif
785
786 if (!gotPicture) {
787 PTRACE(3, m_prefix, "Decoded "<< bytesDecoded << " bytes without getting a Picture");
788 return true;
789 }
790
791 PTRACE(5, m_prefix, "Decoded " << bytesDecoded << " bytes" << ", Resolution: " << m_context->width << "x" << m_context->height);
792
793 // if decoded frame size is not legal, request an I-Frame
794 if (m_context->width <= 0 || m_context->height <= 0) {
795 PTRACE(1, m_prefix, "Received frame with invalid size");
796 flags = PluginCodec_ReturnCoderRequestIFrame;
797 return true;
798 }
799
800 size_t frameBytes = (m_context->width * m_context->height * 12) / 8;
801 if (dstRTP.GetPayloadSize() - sizeof(PluginCodec_Video_FrameHeader) < frameBytes) {
802 PTRACE(2, m_prefix, "Destination buffer size " << dstRTP.GetPayloadSize() << " too small for frame of size " << m_context->width << "x" << m_context->height);
803 flags = PluginCodec_ReturnCoderBufferTooSmall;
804 return true;
805 }
806
807 PluginCodec_Video_FrameHeader * header = (PluginCodec_Video_FrameHeader *)dstRTP.GetPayloadPtr();
808 header->x = header->y = 0;
809 header->width = m_context->width;
810 header->height = m_context->height;
811 int size = m_context->width * m_context->height;
812 if (m_outputFrame->data[1] == m_outputFrame->data[0] + size
813 && m_outputFrame->data[2] == m_outputFrame->data[1] + (size >> 2)) {
814 memcpy(OPAL_VIDEO_FRAME_DATA_PTR(header), m_outputFrame->data[0], frameBytes);
815 } else {
816 BYTE *dst = OPAL_VIDEO_FRAME_DATA_PTR(header);
817 for (int i=0; i<3; i ++) {
818 BYTE *src = m_outputFrame->data[i];
819 int dst_stride = i ? m_context->width >> 1 : m_context->width;
820 int src_stride = m_outputFrame->linesize[i];
821 int h = i ? m_context->height >> 1 : m_context->height;
822
823 if (src_stride==dst_stride) {
824 memcpy(dst, src, dst_stride*h);
825 dst += dst_stride*h;
826 } else {
827 while (h--) {
828 memcpy(dst, src, dst_stride);
829 dst += dst_stride;
830 src += src_stride;
831 }
832 }
833 }
834 }
835
836 dstRTP.SetPayloadSize(sizeof(PluginCodec_Video_FrameHeader) + frameBytes);
837 dstRTP.SetTimestamp(srcRTP.GetTimestamp());
838 dstRTP.SetMarker(true);
839
840 dstLen = dstRTP.GetFrameLen();
841
842 flags |= PluginCodec_ReturnCoderLastFrame;
843
844 return true;
845 }
846
847
848 ///////////////////////////////////////////////////////////////////////////////////
849
H263_RFC2429_DecoderContext()850 H263_RFC2429_DecoderContext::H263_RFC2429_DecoderContext()
851 : H263_Base_DecoderContext("H.263-RFC2429", new RFC2429Frame)
852 {
853 }
854
855 /////////////////////////////////////////////////////////////////////////////
856
H263_RFC2190_DecoderContext()857 H263_RFC2190_DecoderContext::H263_RFC2190_DecoderContext()
858 : H263_Base_DecoderContext("H.263-RFC2190", new RFC2190Depacketizer)
859 {
860 }
861
862 /////////////////////////////////////////////////////////////////////////////
863
get_codec_options(const struct PluginCodec_Definition * codec,void *,const char *,void * parm,unsigned * parmLen)864 static int get_codec_options(const struct PluginCodec_Definition * codec,
865 void *,
866 const char *,
867 void * parm,
868 unsigned * parmLen)
869 {
870 if (parmLen == NULL || parm == NULL || *parmLen != sizeof(struct PluginCodec_Option **))
871 return 0;
872
873 *(const void **)parm = codec->userData;
874 *parmLen = 0; //FIXME
875 return 1;
876 }
877
free_codec_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)878 static int free_codec_options ( const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
879 {
880 if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
881 return 0;
882
883 char ** strings = (char **) parm;
884 for (char ** string = strings; *string != NULL; string++)
885 free(*string);
886 free(strings);
887 return 1;
888 }
889
890
891 /////////////////////////////////////////////////////////////////////////////
892
create_encoder(const struct PluginCodec_Definition * codec)893 static void * create_encoder(const struct PluginCodec_Definition * codec)
894 {
895 H263_Base_EncoderContext * context;
896
897 if (strcmp(codec->destFormat, h263Desc) == 0)
898 context = new H263_RFC2190_EncoderContext();
899 else
900 context = new H263_RFC2429_EncoderContext();
901
902 if (context->Init())
903 return context;
904
905 delete context;
906 return NULL;
907 }
908
destroy_encoder(const struct PluginCodec_Definition *,void * context)909 static void destroy_encoder(const struct PluginCodec_Definition * /*codec*/, void * context)
910 {
911 delete (H263_Base_EncoderContext *)context;
912 }
913
codec_encoder(const struct PluginCodec_Definition *,void * context,const void * from,unsigned * fromLen,void * to,unsigned * toLen,unsigned int * flag)914 static int codec_encoder(const struct PluginCodec_Definition * ,
915 void * context,
916 const void * from,
917 unsigned * fromLen,
918 void * to,
919 unsigned * toLen,
920 unsigned int * flag)
921 {
922 return ((H263_Base_EncoderContext *)context)->EncodeFrames((const BYTE *)from, *fromLen, (BYTE *)to, *toLen, *flag);
923 }
924
925 #define PMAX(a,b) ((a)>=(b)?(a):(b))
926 #define PMIN(a,b) ((a)<=(b)?(a):(b))
927
FindBoundingBox(const char * const ** parm,int * mpi,int & minWidth,int & minHeight,int & maxWidth,int & maxHeight,int & frameTime,int & targetBitRate,int & maxBitRate)928 static void FindBoundingBox(const char * const * * parm,
929 int * mpi,
930 int & minWidth,
931 int & minHeight,
932 int & maxWidth,
933 int & maxHeight,
934 int & frameTime,
935 int & targetBitRate,
936 int & maxBitRate)
937 {
938 // initialise the MPI values to disabled
939 int i;
940 for (i = 0; i < 5; i++)
941 mpi[i] = PLUGINCODEC_MPI_DISABLED;
942
943 // following values will be set while scanning for options
944 minWidth = INT_MAX;
945 minHeight = INT_MAX;
946 maxWidth = 0;
947 maxHeight = 0;
948 int rxMinWidth = QCIF_WIDTH;
949 int rxMinHeight = QCIF_HEIGHT;
950 int rxMaxWidth = QCIF_WIDTH;
951 int rxMaxHeight = QCIF_HEIGHT;
952 int frameRate = 10; // 10 fps
953 int origFrameTime = 900; // 10 fps in video RTP timestamps
954 int maxBR = 0;
955 maxBitRate = 0;
956 targetBitRate = 0;
957
958 // extract the MPI values set in the custom options, and find the min/max of them
959 frameTime = 0;
960
961 for (const char * const * option = *parm; *option != NULL; option += 2) {
962 if (STRCMPI(option[0], "MaxBR") == 0)
963 maxBR = atoi(option[1]);
964 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MAX_BIT_RATE) == 0)
965 maxBitRate = atoi(option[1]);
966 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
967 targetBitRate = atoi(option[1]);
968 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MIN_RX_FRAME_WIDTH) == 0)
969 rxMinWidth = atoi(option[1]);
970 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MIN_RX_FRAME_HEIGHT) == 0)
971 rxMinHeight = atoi(option[1]);
972 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MAX_RX_FRAME_WIDTH) == 0)
973 rxMaxWidth = atoi(option[1]);
974 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_MAX_RX_FRAME_HEIGHT) == 0)
975 rxMaxHeight = atoi(option[1]);
976 else if (STRCMPI(option[0], PLUGINCODEC_OPTION_FRAME_TIME) == 0)
977 origFrameTime = atoi(option[1]);
978 else {
979 for (i = 0; i < 5; i++) {
980 if (STRCMPI(option[0], StandardVideoSizes[i].optionName) == 0) {
981 mpi[i] = atoi(option[1]);
982 if (mpi[i] != PLUGINCODEC_MPI_DISABLED) {
983 int thisTime = 3003*mpi[i];
984 if (minWidth > StandardVideoSizes[i].width)
985 minWidth = StandardVideoSizes[i].width;
986 if (minHeight > StandardVideoSizes[i].height)
987 minHeight = StandardVideoSizes[i].height;
988 if (maxWidth < StandardVideoSizes[i].width)
989 maxWidth = StandardVideoSizes[i].width;
990 if (maxHeight < StandardVideoSizes[i].height)
991 maxHeight = StandardVideoSizes[i].height;
992 if (thisTime > frameTime)
993 frameTime = thisTime;
994 }
995 }
996 }
997 }
998 }
999
1000 // if no MPIs specified, then the spec says to use QCIF
1001 if (frameTime == 0) {
1002 int ft;
1003 if (frameRate != 0)
1004 ft = 90000 / frameRate;
1005 else
1006 ft = origFrameTime;
1007 mpi[1] = (ft + 1502) / 3003;
1008
1009 #ifdef DEFAULT_TO_FULL_CAPABILITIES
1010 minWidth = QCIF_WIDTH;
1011 maxWidth = CIF16_WIDTH;
1012 minHeight = QCIF_HEIGHT;
1013 maxHeight = CIF16_HEIGHT;
1014 #else
1015 minWidth = maxWidth = QCIF_WIDTH;
1016 minHeight = maxHeight = QCIF_HEIGHT;
1017 #endif
1018 }
1019
1020 // find the smallest MPI size that is larger than the min frame size
1021 for (i = 0; i < 5; i++) {
1022 if (StandardVideoSizes[i].width >= rxMinWidth && StandardVideoSizes[i].height >= rxMinHeight) {
1023 rxMinWidth = StandardVideoSizes[i].width;
1024 rxMinHeight = StandardVideoSizes[i].height;
1025 break;
1026 }
1027 }
1028
1029 // find the largest MPI size that is smaller than the max frame size
1030 for (i = 4; i >= 0; i--) {
1031 if (StandardVideoSizes[i].width <= rxMaxWidth && StandardVideoSizes[i].height <= rxMaxHeight) {
1032 rxMaxWidth = StandardVideoSizes[i].width;
1033 rxMaxHeight = StandardVideoSizes[i].height;
1034 break;
1035 }
1036 }
1037
1038 // the final min/max is the smallest bounding box that will enclose both the MPI information and the min/max information
1039 minWidth = PMAX(rxMinWidth, minWidth);
1040 maxWidth = PMIN(rxMaxWidth, maxWidth);
1041 minHeight = PMAX(rxMinHeight, minHeight);
1042 maxHeight = PMIN(rxMaxHeight, maxHeight);
1043
1044 // turn off any MPI that are outside the final bounding box
1045 for (i = 0; i < 5; i++) {
1046 if (StandardVideoSizes[i].width < minWidth ||
1047 StandardVideoSizes[i].width > maxWidth ||
1048 StandardVideoSizes[i].height < minHeight ||
1049 StandardVideoSizes[i].height > maxHeight)
1050 mpi[i] = PLUGINCODEC_MPI_DISABLED;
1051 }
1052
1053 // find an appropriate max bit rate
1054 if (maxBitRate == 0) {
1055 if (maxBR != 0)
1056 maxBitRate = maxBR * 100;
1057 else if (targetBitRate != 0)
1058 maxBitRate = targetBitRate;
1059 else
1060 maxBitRate = 327000;
1061 }
1062 else if (maxBR > 0)
1063 maxBitRate = PMIN(maxBR * 100, maxBitRate);
1064
1065 if (targetBitRate == 0)
1066 targetBitRate = 327000;
1067 }
1068
to_normalised_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)1069 static int to_normalised_options(const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
1070 {
1071 if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
1072 return 0;
1073
1074 // find bounding box enclosing all MPI values
1075 int mpi[5];
1076 int minWidth, minHeight, maxHeight, maxWidth, frameTime, targetBitRate, maxBitRate;
1077 FindBoundingBox((const char * const * *)parm, mpi, minWidth, minHeight, maxWidth, maxHeight, frameTime, targetBitRate, maxBitRate);
1078
1079 char ** options = (char **)calloc(16+(5*2)+2, sizeof(char *));
1080 *(char ***)parm = options;
1081 if (options == NULL)
1082 return 0;
1083
1084 options[ 0] = strdup(PLUGINCODEC_OPTION_MIN_RX_FRAME_WIDTH);
1085 options[ 1] = num2str(minWidth);
1086 options[ 2] = strdup(PLUGINCODEC_OPTION_MIN_RX_FRAME_HEIGHT);
1087 options[ 3] = num2str(minHeight);
1088 options[ 4] = strdup(PLUGINCODEC_OPTION_MAX_RX_FRAME_WIDTH);
1089 options[ 5] = num2str(maxWidth);
1090 options[ 6] = strdup(PLUGINCODEC_OPTION_MAX_RX_FRAME_HEIGHT);
1091 options[ 7] = num2str(maxHeight);
1092 options[ 8] = strdup(PLUGINCODEC_OPTION_FRAME_TIME);
1093 options[ 9] = num2str(frameTime);
1094 options[10] = strdup(PLUGINCODEC_OPTION_MAX_BIT_RATE);
1095 options[11] = num2str(maxBitRate);
1096 options[12] = strdup(PLUGINCODEC_OPTION_TARGET_BIT_RATE);
1097 options[13] = num2str(targetBitRate);
1098 options[14] = strdup("MaxBR");
1099 options[15] = num2str((maxBitRate+50)/100);
1100 for (int i = 0; i < 5; i++) {
1101 options[16+i*2] = strdup(StandardVideoSizes[i].optionName);
1102 options[16+i*2+1] = num2str(mpi[i]);
1103 }
1104
1105 return 1;
1106 }
1107
to_customised_options(const struct PluginCodec_Definition *,void *,const char *,void * parm,unsigned * parmLen)1108 static int to_customised_options(const struct PluginCodec_Definition *, void *, const char *, void * parm, unsigned * parmLen)
1109 {
1110 if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***))
1111 return 0;
1112
1113 // find bounding box enclosing all MPI values
1114 int mpi[5];
1115 int minWidth, minHeight, maxHeight, maxWidth, frameTime, targetBitRate, maxBitRate;
1116 FindBoundingBox((const char * const * *)parm, mpi, minWidth, minHeight, maxWidth, maxHeight, frameTime, targetBitRate, maxBitRate);
1117
1118 char ** options = (char **)calloc(14+5*2+2, sizeof(char *));
1119 *(char ***)parm = options;
1120 if (options == NULL)
1121 return 0;
1122
1123 options[ 0] = strdup(PLUGINCODEC_OPTION_MIN_RX_FRAME_WIDTH);
1124 options[ 1] = num2str(minWidth);
1125 options[ 2] = strdup(PLUGINCODEC_OPTION_MIN_RX_FRAME_HEIGHT);
1126 options[ 3] = num2str(minHeight);
1127 options[ 4] = strdup(PLUGINCODEC_OPTION_MAX_RX_FRAME_WIDTH);
1128 options[ 5] = num2str(maxWidth);
1129 options[ 6] = strdup(PLUGINCODEC_OPTION_MAX_RX_FRAME_HEIGHT);
1130 options[ 7] = num2str(maxHeight);
1131 options[ 8] = strdup(PLUGINCODEC_OPTION_MAX_BIT_RATE);
1132 options[ 9] = num2str(maxBitRate);
1133 options[10] = strdup(PLUGINCODEC_OPTION_TARGET_BIT_RATE);
1134 options[11] = num2str(targetBitRate);
1135 options[12] = strdup("MaxBR");
1136 options[13] = num2str((maxBitRate+50)/100);
1137 for (int i = 0; i < 5; i++) {
1138 options[14+i*2] = strdup(StandardVideoSizes[i].optionName);
1139 options[14+i*2+1] = num2str(mpi[i]);
1140 }
1141
1142 return 1;
1143 }
1144
encoder_set_options(const PluginCodec_Definition *,void * context,const char *,void * parm,unsigned * parmLen)1145 static int encoder_set_options(const PluginCodec_Definition *,
1146 void * context,
1147 const char * ,
1148 void * parm,
1149 unsigned * parmLen)
1150 {
1151 if (parmLen == NULL || *parmLen != sizeof(const char **) || parm == NULL)
1152 return 0;
1153
1154 return ((H263_Base_EncoderContext *)context)->SetOptions((const char * const *)parm);
1155 }
1156
encoder_get_output_data_size(const PluginCodec_Definition *,void *,const char *,void *,unsigned *)1157 static int encoder_get_output_data_size(const PluginCodec_Definition *, void *, const char *, void *, unsigned *)
1158 {
1159 return PluginCodec_RTP_MaxPayloadSize;
1160 }
1161
1162
GetCustomMPI(const char * str,unsigned width[MAX_H263_CUSTOM_SIZES],unsigned height[MAX_H263_CUSTOM_SIZES],unsigned mpi[MAX_H263_CUSTOM_SIZES],size_t & count)1163 static bool GetCustomMPI(const char * str,
1164 unsigned width[MAX_H263_CUSTOM_SIZES],
1165 unsigned height[MAX_H263_CUSTOM_SIZES],
1166 unsigned mpi[MAX_H263_CUSTOM_SIZES],
1167 size_t & count)
1168 {
1169 count = 0;
1170 for (;;) {
1171 width[count] = height[count] = mpi[count] = 0;
1172
1173 char * end;
1174 width[count] = strtoul(str, &end, 10);
1175 if (*end != ',')
1176 return false;
1177
1178 str = end+1;
1179 height[count] = strtoul(str, &end, 10);
1180 if (*end != ',')
1181 return false;
1182
1183 str = end+1;
1184 mpi[count] = strtoul(str, &end, 10);
1185 if (mpi[count] == 0 || mpi[count] > PLUGINCODEC_MPI_DISABLED)
1186 return false;
1187
1188 if (mpi[count] < PLUGINCODEC_MPI_DISABLED && (width[count] < 16 || height[count] < 16))
1189 return false;
1190
1191 if (mpi[count] == 0 || mpi[count] > PLUGINCODEC_MPI_DISABLED)
1192 return false;
1193 ++count;
1194 if (count >= MAX_H263_CUSTOM_SIZES || *end != ';')
1195 return true;
1196
1197 str = end+1;
1198 }
1199 }
1200
1201
MergeCustomH263(char ** result,const char * dest,const char * src)1202 static int MergeCustomH263(char ** result, const char * dest, const char * src)
1203 {
1204 size_t srcCount;
1205 unsigned srcWidth[MAX_H263_CUSTOM_SIZES], srcHeight[MAX_H263_CUSTOM_SIZES], srcMPI[MAX_H263_CUSTOM_SIZES];
1206 if (!GetCustomMPI(src, srcWidth, srcHeight, srcMPI, srcCount)) {
1207 PTRACE(2, "IPP-H.263", "Invalid source custom MPI format \"" << src << '"');
1208 return false;
1209 }
1210
1211 size_t dstCount;
1212 unsigned dstWidth[MAX_H263_CUSTOM_SIZES], dstHeight[MAX_H263_CUSTOM_SIZES], dstMPI[MAX_H263_CUSTOM_SIZES];
1213 if (!GetCustomMPI(dest, dstWidth, dstHeight, dstMPI, dstCount)) {
1214 PTRACE(2, "IPP-H.263", "Invalid destination custom MPI format \"" << dest << '"');
1215 return false;
1216 }
1217
1218 size_t resultCount = 0;
1219 unsigned resultWidth[MAX_H263_CUSTOM_SIZES];
1220 unsigned resultHeight[MAX_H263_CUSTOM_SIZES];
1221 unsigned resultMPI[MAX_H263_CUSTOM_SIZES];
1222
1223 for (size_t s = 0; s < srcCount; ++s) {
1224 for (size_t d = 0; d < dstCount; ++d) {
1225 if (srcWidth[s] == dstWidth[d] && srcHeight[s] == dstHeight[d]) {
1226 resultWidth[resultCount] = srcWidth[s];
1227 resultHeight[resultCount] = srcHeight[s];
1228 resultMPI[resultCount] = std::max(srcMPI[s], dstMPI[d]);
1229 ++resultCount;
1230 }
1231 }
1232 }
1233
1234 if (resultCount == 0)
1235 *result = strdup(DEFAULT_CUSTOM_MPI);
1236 else {
1237 size_t len = 0;
1238 char buffer[MAX_H263_CUSTOM_SIZES*20];
1239 for (size_t i = 0; i < resultCount; ++i)
1240 len += sprintf(&buffer[len], len == 0 ? "%u,%u,%u" : ";%u,%u,%u", resultWidth[i], resultHeight[i], resultMPI[i]);
1241 *result = strdup(buffer);
1242 }
1243
1244 return true;
1245 }
1246
1247
FreeString(char * str)1248 static void FreeString(char * str)
1249 {
1250 free(str);
1251 }
1252
1253
1254 /////////////////////////////////////////////////////////////////////////////
1255
create_decoder(const struct PluginCodec_Definition * codec)1256 static void * create_decoder(const struct PluginCodec_Definition * codec)
1257 {
1258 H263_Base_DecoderContext * decoder;
1259 if (strcmp(codec->sourceFormat, h263Desc) == 0)
1260 decoder = new H263_RFC2190_DecoderContext();
1261 else
1262 decoder = new H263_RFC2429_DecoderContext();
1263
1264 if (decoder->OpenCodec()) // decoder will re-initialise context with correct frame size
1265 return decoder;
1266
1267 delete decoder;
1268 return NULL;
1269 }
1270
destroy_decoder(const struct PluginCodec_Definition *,void * context)1271 static void destroy_decoder(const struct PluginCodec_Definition * /*codec*/, void * context)
1272 {
1273 delete (H263_Base_DecoderContext *)context;
1274 }
1275
codec_decoder(const struct PluginCodec_Definition *,void * context,const void * from,unsigned * fromLen,void * to,unsigned * toLen,unsigned int * flag)1276 static int codec_decoder(const struct PluginCodec_Definition *,
1277 void * context,
1278 const void * from,
1279 unsigned * fromLen,
1280 void * to,
1281 unsigned * toLen,
1282 unsigned int * flag)
1283 {
1284 return ((H263_Base_DecoderContext *)context)->DecodeFrames((const BYTE *)from, *fromLen, (BYTE *)to, *toLen, *flag) ? 1 : 0;
1285 }
1286
decoder_get_output_data_size(const PluginCodec_Definition * codec,void *,const char *,void *,unsigned *)1287 static int decoder_get_output_data_size(const PluginCodec_Definition * codec, void *, const char *, void *, unsigned *)
1288 {
1289 return sizeof(PluginCodec_Video_FrameHeader) + ((codec->parm.video.maxFrameWidth * codec->parm.video.maxFrameHeight * 3) / 2);
1290 }
1291
decoder_set_options(const PluginCodec_Definition *,void * context,const char *,void * parm,unsigned * parmLen)1292 static int decoder_set_options(const PluginCodec_Definition *,
1293 void * context,
1294 const char * ,
1295 void * parm,
1296 unsigned * parmLen)
1297 {
1298 if (parmLen == NULL || *parmLen != sizeof(const char **) || parm == NULL)
1299 return 0;
1300
1301 return ((H263_Base_DecoderContext *)context)->SetOptions((const char * const *)parm);
1302 }
1303
1304 PLUGINCODEC_CONTROL_LOG_FUNCTION_DEF
1305
1306 /////////////////////////////////////////////////////////////////////////////
1307
1308 static struct PluginCodec_information licenseInfo = {
1309 1145863600, // timestamp = Mon 24 Apr 2006 07:26:40 AM UTC
1310
1311 "Matthias Schneider, Craig Southeren" // source code author
1312 "Guilhem Tardy, Derek Smithies",
1313 "1.0", // source code version
1314 "openh323@openh323.org", // source code email
1315 "http://sourceforge.net/projects/openh323", // source code URL
1316 "Copyright (C) 2007 Matthias Schneider" // source code copyright
1317 ", Copyright (C) 2006 by Post Increment"
1318 ", Copyright (C) 2005 Salyens"
1319 ", Copyright (C) 2001 March Networks Corporation"
1320 ", Copyright (C) 1999-2000 Equivalence Pty. Ltd.",
1321 "MPL 1.0", // source code license
1322 PluginCodec_License_MPL, // source code license
1323
1324 "FFMPEG", // codec description
1325 "Michael Niedermayer, Fabrice Bellard", // codec author
1326 "", // codec version
1327 "ffmpeg-devel-request@mplayerhq.hu", // codec email
1328 "http://ffmpeg.mplayerhq.hu", // codec URL
1329 "Copyright (c) 2000-2001 Fabrice Bellard" // codec copyright information
1330 ", Copyright (c) 2002-2003 Michael Niedermayer",
1331 "GNU LESSER GENERAL PUBLIC LICENSE, Version 2.1, February 1999", // codec license
1332 PluginCodec_License_LGPL // codec license code
1333 };
1334
1335 static const char SQCIF_MPI[] = PLUGINCODEC_SQCIF_MPI;
1336 static const char QCIF_MPI[] = PLUGINCODEC_QCIF_MPI;
1337 static const char CIF_MPI[] = PLUGINCODEC_CIF_MPI;
1338 static const char CIF4_MPI[] = PLUGINCODEC_CIF4_MPI;
1339 static const char CIF16_MPI[] = PLUGINCODEC_CIF16_MPI;
1340
1341 static PluginCodec_ControlDefn EncoderControls[] = {
1342 { PLUGINCODEC_CONTROL_GET_CODEC_OPTIONS, get_codec_options },
1343 { PLUGINCODEC_CONTROL_FREE_CODEC_OPTIONS, free_codec_options },
1344 { PLUGINCODEC_CONTROL_TO_NORMALISED_OPTIONS, to_normalised_options },
1345 { PLUGINCODEC_CONTROL_TO_CUSTOMISED_OPTIONS, to_customised_options },
1346 { PLUGINCODEC_CONTROL_SET_CODEC_OPTIONS, encoder_set_options },
1347 { PLUGINCODEC_CONTROL_GET_OUTPUT_DATA_SIZE, encoder_get_output_data_size },
1348 PLUGINCODEC_CONTROL_LOG_FUNCTION_INC
1349 { NULL }
1350 };
1351
1352 static PluginCodec_ControlDefn DecoderControls[] = {
1353 { PLUGINCODEC_CONTROL_GET_CODEC_OPTIONS, get_codec_options },
1354 { PLUGINCODEC_CONTROL_SET_CODEC_OPTIONS, decoder_set_options },
1355 { PLUGINCODEC_CONTROL_GET_OUTPUT_DATA_SIZE, decoder_get_output_data_size },
1356 { NULL }
1357 };
1358
1359 static struct PluginCodec_Option const sqcifMPI =
1360 {
1361 PluginCodec_IntegerOption, // Option type
1362 SQCIF_MPI, // User visible name
1363 false, // User Read/Only flag
1364 PluginCodec_MaxMerge, // Merge mode
1365 "1", // Initial value
1366 "SQCIF", // FMTP option name
1367 STRINGIZE(PLUGINCODEC_MPI_DISABLED), // FMTP default value
1368 0, // H.245 generic capability code and bit mask
1369 "1", // Minimum value
1370 STRINGIZE(PLUGINCODEC_MPI_DISABLED) // Maximum value
1371 };
1372
1373 static struct PluginCodec_Option const qcifMPI =
1374 {
1375 PluginCodec_IntegerOption, // Option type
1376 QCIF_MPI, // User visible name
1377 false, // User Read/Only flag
1378 PluginCodec_MaxMerge, // Merge mode
1379 "1", // Initial value
1380 "QCIF", // FMTP option name
1381 STRINGIZE(PLUGINCODEC_MPI_DISABLED), // FMTP default value
1382 0, // H.245 generic capability code and bit mask
1383 "1", // Minimum value
1384 STRINGIZE(PLUGINCODEC_MPI_DISABLED) // Maximum value
1385 };
1386
1387 static struct PluginCodec_Option const cifMPI =
1388 {
1389 PluginCodec_IntegerOption, // Option type
1390 CIF_MPI, // User visible name
1391 false, // User Read/Only flag
1392 PluginCodec_MaxMerge, // Merge mode
1393 "1", // Initial value
1394 "CIF", // FMTP option name
1395 STRINGIZE(PLUGINCODEC_MPI_DISABLED), // FMTP default value
1396 0, // H.245 generic capability code and bit mask
1397 "1", // Minimum value
1398 STRINGIZE(PLUGINCODEC_MPI_DISABLED) // Maximum value
1399 };
1400
1401 static struct PluginCodec_Option const cif4MPI =
1402 {
1403 PluginCodec_IntegerOption, // Option type
1404 CIF4_MPI, // User visible name
1405 false, // User Read/Only flag
1406 PluginCodec_MaxMerge, // Merge mode
1407 "1", // Initial value
1408 "CIF4", // FMTP option name
1409 STRINGIZE(PLUGINCODEC_MPI_DISABLED), // FMTP default value
1410 0, // H.245 generic capability code and bit mask
1411 "1", // Minimum value
1412 STRINGIZE(PLUGINCODEC_MPI_DISABLED) // Maximum value
1413 };
1414
1415 static struct PluginCodec_Option const cif16MPI =
1416 {
1417 PluginCodec_IntegerOption, // Option type
1418 CIF16_MPI, // User visible name
1419 false, // User Read/Only flag
1420 PluginCodec_MaxMerge, // Merge mode
1421 "1", // Initial value
1422 "CIF16", // FMTP option name
1423 STRINGIZE(PLUGINCODEC_MPI_DISABLED), // FMTP default value
1424 0, // H.245 generic capability code and bit mask
1425 "1", // Minimum value
1426 STRINGIZE(PLUGINCODEC_MPI_DISABLED) // Maximum value
1427 };
1428
1429 static struct PluginCodec_Option const maxBR =
1430 {
1431 PluginCodec_IntegerOption, // Option type
1432 "MaxBR", // User visible name
1433 false, // User Read/Only flag
1434 PluginCodec_MinMerge, // Merge mode
1435 "0", // Initial value
1436 "maxbr", // FMTP option name
1437 "0", // FMTP default value
1438 0, // H.245 generic capability code and bit mask
1439 "0", // Minimum value
1440 "32767" // Maximum value
1441 };
1442
1443 static struct PluginCodec_Option const mediaPacketization =
1444 {
1445 PluginCodec_StringOption, // Option type
1446 PLUGINCODEC_MEDIA_PACKETIZATION, // User visible name
1447 true, // User Read/Only flag
1448 PluginCodec_EqualMerge, // Merge mode
1449 "RFC2190" // Initial value
1450 };
1451
1452 static struct PluginCodec_Option const mediaPacketizationPlus =
1453 {
1454 PluginCodec_StringOption, // Option type
1455 PLUGINCODEC_MEDIA_PACKETIZATIONS, // User visible name
1456 true, // User Read/Only flag
1457 PluginCodec_IntersectionMerge, // Merge mode
1458 "RFC2429,RFC2190" // Initial value
1459 };
1460
1461 static struct PluginCodec_Option const customMPI =
1462 {
1463 PluginCodec_StringOption, // Option type
1464 PLUGINCODEC_CUSTOM_MPI, // User visible name
1465 false, // User Read/Only flag
1466 PluginCodec_CustomMerge, // Merge mode
1467 DEFAULT_CUSTOM_MPI, // Initial value
1468 "CUSTOM", // FMTP option name
1469 DEFAULT_CUSTOM_MPI, // FMTP default value
1470 0, // H.245 generic capability code and bit mask
1471 NULL, // Minimum value
1472 NULL, // Maximum value
1473 MergeCustomH263,
1474 FreeString
1475 };
1476
1477 static struct PluginCodec_Option const annexF =
1478 { PluginCodec_BoolOption, H263_ANNEX_F, false, PluginCodec_AndMerge, "1", "F", "0" };
1479
1480 static struct PluginCodec_Option const annexI =
1481 { PluginCodec_BoolOption, H263_ANNEX_I, false, PluginCodec_AndMerge, "1", "I", "0" };
1482
1483 static struct PluginCodec_Option const annexJ =
1484 { PluginCodec_BoolOption, H263_ANNEX_J, false, PluginCodec_AndMerge, "1", "J", "0" };
1485
1486 static struct PluginCodec_Option const annexK =
1487 { PluginCodec_IntegerOption, H263_ANNEX_K, false, PluginCodec_MinMerge, "0", "K", "0", 0, "0", "4" };
1488
1489 static struct PluginCodec_Option const annexN =
1490 { PluginCodec_BoolOption, H263_ANNEX_N, true, PluginCodec_AndMerge, "0", "N", "0" };
1491
1492 static struct PluginCodec_Option const annexT =
1493 { PluginCodec_BoolOption, H263_ANNEX_T, true, PluginCodec_AndMerge, "0", "T", "0" };
1494
1495 static struct PluginCodec_Option const annexD =
1496 { PluginCodec_BoolOption, H263_ANNEX_D, false, PluginCodec_AndMerge, "1", "D", "0" };
1497
1498 static struct PluginCodec_Option const TemporalSpatialTradeOff =
1499 {
1500 PluginCodec_IntegerOption, // Option type
1501 PLUGINCODEC_OPTION_TEMPORAL_SPATIAL_TRADE_OFF, // User visible name
1502 false, // User Read/Only flag
1503 PluginCodec_AlwaysMerge, // Merge mode
1504 "31", // Initial value
1505 NULL, // FMTP option name
1506 NULL, // FMTP default value
1507 0, // H.245 generic capability code and bit mask
1508 "1", // Minimum value
1509 "31" // Maximum value
1510 };
1511
1512 static struct PluginCodec_Option const * const h263POptionTable[] = {
1513 &mediaPacketizationPlus,
1514 &maxBR,
1515 &qcifMPI,
1516 &cifMPI,
1517 &sqcifMPI,
1518 &cif4MPI,
1519 &cif16MPI,
1520 &customMPI,
1521 &annexF,
1522 &annexI,
1523 &annexJ,
1524 &annexK,
1525 &annexN,
1526 &annexT,
1527 &annexD,
1528 &TemporalSpatialTradeOff,
1529 NULL
1530 };
1531
1532
1533 static struct PluginCodec_Option const * const h263OptionTable[] = {
1534 &mediaPacketization,
1535 &maxBR,
1536 &qcifMPI,
1537 &cifMPI,
1538 &sqcifMPI,
1539 &cif4MPI,
1540 &cif16MPI,
1541 &annexF,
1542 NULL
1543 };
1544
1545 /////////////////////////////////////////////////////////////////////////////
1546
1547 static struct PluginCodec_Definition h263CodecDefn[] = {
1548 {
1549 // All frame sizes (dynamic) encoder
1550 PLUGIN_CODEC_VERSION_OPTIONS, // codec API version
1551 &licenseInfo, // license information
1552
1553 PluginCodec_MediaTypeVideo | // audio codec
1554 PluginCodec_RTPTypeShared | // specified RTP type
1555 PluginCodec_RTPTypeDynamic, // specified RTP type
1556
1557 h263PDesc, // text decription
1558 YUV420PDesc, // source format
1559 h263PDesc, // destination format
1560
1561 h263POptionTable, // user data
1562
1563 VIDEO_CLOCKRATE, // samples per second
1564 H263_BITRATE, // raw bits per second
1565 20000, // nanoseconds per frame
1566
1567 {{
1568 CIF4_WIDTH, // frame width
1569 CIF4_HEIGHT, // frame height
1570 10, // recommended frame rate
1571 60, // maximum frame rate
1572 }},
1573
1574 0, // IANA RTP payload code
1575 sdpH263P, // RTP payload name
1576
1577 create_encoder, // create codec function
1578 destroy_encoder, // destroy codec
1579 codec_encoder, // encode/decode
1580 EncoderControls, // codec controls
1581
1582 PluginCodec_H323VideoCodec_h263, // h323CapabilityType
1583 NULL // h323CapabilityData
1584 },
1585 {
1586 // All frame sizes (dynamic) decoder
1587 PLUGIN_CODEC_VERSION_OPTIONS, // codec API version
1588 &licenseInfo, // license information
1589
1590 PluginCodec_MediaTypeVideo | // audio codec
1591 PluginCodec_RTPTypeShared | // specified RTP type
1592 PluginCodec_RTPTypeDynamic, // specified RTP type
1593
1594 h263PDesc, // text decription
1595 h263PDesc, // source format
1596 YUV420PDesc, // destination format
1597
1598 h263POptionTable, // user data
1599
1600 VIDEO_CLOCKRATE, // samples per second
1601 H263_BITRATE, // raw bits per second
1602 20000, // nanoseconds per frame
1603
1604 {{
1605 CIF4_WIDTH, // frame width
1606 CIF4_HEIGHT, // frame height
1607 10, // recommended frame rate
1608 60, // maximum frame rate
1609 }},
1610
1611 0, // IANA RTP payload code
1612 sdpH263P, // RTP payload name
1613
1614 create_decoder, // create codec function
1615 destroy_decoder, // destroy codec
1616 codec_decoder, // encode/decode
1617 DecoderControls, // codec controls
1618
1619 PluginCodec_H323VideoCodec_h263, // h323CapabilityType
1620 NULL // h323CapabilityData
1621 },
1622
1623 {
1624 // H.263 (RFC 2190) encoder
1625 PLUGIN_CODEC_VERSION_OPTIONS, // codec API version
1626 &licenseInfo, // license information
1627
1628 PluginCodec_MediaTypeVideo | // video codec
1629 PluginCodec_MediaTypeExtVideo | // Extended video codec
1630 PluginCodec_RTPTypeExplicit, // specified RTP type
1631
1632 h263Desc, // text decription
1633 YUV420PDesc, // source format
1634 h263Desc, // destination format
1635
1636 h263OptionTable, // user data
1637
1638 VIDEO_CLOCKRATE, // samples per second
1639 H263_BITRATE, // raw bits per second
1640 20000, // nanoseconds per frame
1641
1642 {{
1643 CIF16_WIDTH, // frame width
1644 CIF16_HEIGHT, // frame height
1645 10, // recommended frame rate
1646 60, // maximum frame rate
1647 }},
1648
1649 RTP_RFC2190_PAYLOAD, // IANA RTP payload code
1650 sdpH263, // RTP payload name
1651
1652 create_encoder, // create codec function
1653 destroy_encoder, // destroy codec
1654 codec_encoder, // encode/decode
1655 EncoderControls, // codec controls
1656
1657 PluginCodec_H323VideoCodec_h263, // h323CapabilityType
1658 NULL // h323CapabilityData
1659 },
1660 {
1661 // H.263 (RFC 2190) decoder
1662 PLUGIN_CODEC_VERSION_OPTIONS, // codec API version
1663 &licenseInfo, // license information
1664
1665 PluginCodec_MediaTypeVideo | // video codec
1666 PluginCodec_MediaTypeExtVideo | // Extended video codec
1667 PluginCodec_RTPTypeExplicit, // specified RTP type
1668
1669 h263Desc, // text decription
1670 h263Desc, // source format
1671 YUV420PDesc, // destination format
1672
1673 h263OptionTable, // user data
1674
1675 VIDEO_CLOCKRATE, // samples per second
1676 H263_BITRATE, // raw bits per second
1677 20000, // nanoseconds per frame
1678
1679 {{
1680 CIF16_WIDTH, // frame width
1681 CIF16_HEIGHT, // frame height
1682 10, // recommended frame rate
1683 60, // maximum frame rate
1684 }},
1685 RTP_RFC2190_PAYLOAD, // IANA RTP payload code
1686 sdpH263, // RTP payload name
1687
1688 create_decoder, // create codec function
1689 destroy_decoder, // destroy codec
1690 codec_decoder, // encode/decode
1691 DecoderControls, // codec controls
1692
1693 PluginCodec_H323VideoCodec_h263, // h323CapabilityType
1694 NULL // h323CapabilityData
1695 }
1696
1697 };
1698
1699 /////////////////////////////////////////////////////////////////////////////
1700
1701 extern "C" {
PLUGIN_CODEC_IMPLEMENT(FFMPEG_H263P)1702 PLUGIN_CODEC_IMPLEMENT(FFMPEG_H263P)
1703
1704 PLUGIN_CODEC_DLL_API struct PluginCodec_Definition * PLUGIN_CODEC_GET_CODEC_FN(unsigned * count, unsigned version)
1705 {
1706 if (version < PLUGIN_CODEC_VERSION_OPTIONS) {
1707 *count = 0;
1708 return NULL;
1709 }
1710
1711 *count = sizeof(h263CodecDefn) / sizeof(struct PluginCodec_Definition);
1712 return h263CodecDefn;
1713 }
1714
1715 };
1716