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