1 /*
2  * mediafmt.cxx
3  *
4  * Media Format descriptions
5  *
6  * Open H323 Library
7  *
8  * Copyright (c) 1999-2000 Equivalence Pty. Ltd.
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Open H323 Library.
21  *
22  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 28048 $
27  * $Author: rjongbloed $
28  * $Date: 2012-07-17 22:41:57 -0500 (Tue, 17 Jul 2012) $
29  */
30 
31 #include <ptlib.h>
32 
33 #ifdef __GNUC__
34 #pragma implementation "mediafmt.h"
35 #pragma implementation "mediacmd.h"
36 #endif
37 
38 #include <opal/buildopts.h>
39 
40 #include <opal/mediafmt.h>
41 #include <opal/mediacmd.h>
42 #include <codec/opalplugin.h>
43 #include <codec/opalwavfile.h>
44 #include <ptlib/videoio.h>
45 #include <ptclib/cypher.h>
46 
47 
48 #define new PNEW
49 
50 
51 /////////////////////////////////////////////////////////////////////////////
52 
53 #define AUDIO_FORMAT(name, rtpPayloadType, encodingName, frameSize, frameTime, rxFrames, txFrames, maxFrames, clock) \
54   const OpalAudioFormat & GetOpal##name() \
55   { \
56     static const OpalAudioFormat name(OPAL_##name, RTP_DataFrame::rtpPayloadType, \
57                                     encodingName, frameSize, frameTime, rxFrames, txFrames, maxFrames, clock); \
58     return name; \
59   }
60 //           name            rtpPayloadType  encodingName frameSize frameTime rxFrames txFrames maxFrames clock
61 AUDIO_FORMAT(PCM16,          MaxPayloadType, "",          16,        8,       240,      0,      256,       8000);
62 AUDIO_FORMAT(PCM16_16KHZ,    MaxPayloadType, "",          32,       16,       240,      0,      256,      16000);
63 AUDIO_FORMAT(PCM16_32KHZ,    MaxPayloadType, "",          64,       32,       240,      0,      256,      32000);
64 AUDIO_FORMAT(PCM16_48KHZ,    MaxPayloadType, "",          96,       48,       240,      0,      256,      48000);
65 AUDIO_FORMAT(L16_MONO_8KHZ,  L16_Mono,       "L16",       16,        8,       240,     30,      256,       8000);
66 AUDIO_FORMAT(L16_MONO_16KHZ, L16_Mono,       "L16",       32,       16,       240,     30,      256,      16000);
67 AUDIO_FORMAT(L16_MONO_32KHZ, L16_Mono,       "L16",       64,       32,       240,     30,      256,      32000);
68 AUDIO_FORMAT(L16_MONO_48KHZ, L16_Mono,       "L16",       96,       48,       240,     30,      256,      48000);
69 AUDIO_FORMAT(G711_ULAW_64K,  PCMU,           "PCMU",       8,        8,       240,     20,      256,       8000);
70 AUDIO_FORMAT(G711_ALAW_64K,  PCMA,           "PCMA",       8,        8,       240,     20,      256,       8000);
71 
72 
73 class OpalStereoAudioFormat : public OpalAudioFormat
74 {
75 public:
OpalStereoAudioFormat(const char * fullName,RTP_DataFrame::PayloadTypes rtpPayloadType,const char * encodingName,PINDEX frameSize,unsigned frameTime,unsigned rxFrames,unsigned txFrames,unsigned maxFrames,unsigned clockRate)76   OpalStereoAudioFormat(const char * fullName,
77                         RTP_DataFrame::PayloadTypes rtpPayloadType,
78                         const char * encodingName,
79                         PINDEX   frameSize,
80                         unsigned frameTime,
81                         unsigned rxFrames,
82                         unsigned txFrames,
83                         unsigned maxFrames,
84                         unsigned clockRate)
85     : OpalAudioFormat(fullName, rtpPayloadType, encodingName, frameSize, frameTime, rxFrames, txFrames, maxFrames, clockRate)
86   {
87     SetOptionInteger(OpalAudioFormat::ChannelsOption(), 2);
88   }
89 };
90 
GetOpalPCM16S()91 const OpalAudioFormat & GetOpalPCM16S()
92 {
93   static OpalStereoAudioFormat stereo8k(OPAL_PCM16S,        		// name of the media format
94 					RTP_DataFrame::MaxPayloadType,	// RTP payload code
95 					"",				// encoding name
96 					64,				// frame size in bytes
97 					16,				// frame time (1 ms in clock units)
98 					240,				// recommended rx frames/packet
99 					0,				// recommended tx frames/packet
100 					256,				// max tx frame size
101 					8000);				// clock rate
102   return stereo8k;
103 };
104 
GetOpalPCM16S_16KHZ()105 const OpalAudioFormat & GetOpalPCM16S_16KHZ()
106 {
107   static OpalStereoAudioFormat stereo16k(OPAL_PCM16S_16KHZ,		// name of the media format
108 					RTP_DataFrame::MaxPayloadType,	// RTP payload code
109 					"",				// encoding name
110 					64,				// frame size in bytes
111 					16,				// frame time (1 ms in clock units)
112 					240,				// recommended rx frames/packet
113 					0,				// recommended tx frames/packet
114 					256,				// max tx frame size
115 					16000);				// clock rate
116   return stereo16k;
117 };
118 
GetOpalL16_STEREO_16KHZ()119 const OpalAudioFormat & GetOpalL16_STEREO_16KHZ()
120 {
121   static OpalStereoAudioFormat stereo16k(OPAL_L16_STEREO_16KHZ,         // name of the media format
122 					RTP_DataFrame::L16_Stereo,      // RTP payload code
123 					"L16S",                         // encoding name
124 					64,                             // frame size in bytes
125 					16,                             // frame time (1 ms in clock units)
126 					240,                            // recommended rx frames/packet
127 					30,                             // recommended tx frames/packet
128 					256,                            // max tx frame size
129 					16000);                         // clock rate
130   return stereo16k;
131 };
132 
GetOpalPCM16S_32KHZ()133 const OpalAudioFormat & GetOpalPCM16S_32KHZ()
134 {
135   static OpalStereoAudioFormat stereo32k(OPAL_PCM16S_32KHZ,		// name of the media format
136 					RTP_DataFrame::MaxPayloadType,	// RTP payload code
137 					"",				// encoding name
138 					128,				// frame size in bytes
139 					32,				// frame time (1 ms in clock units)
140 					240,				// recommended rx frames/packet
141 					0,				// recommended tx frames/packet
142 					256,				// max tx frame size
143 					32000);				// clock rate
144   return stereo32k;
145 };
146 
GetOpalL16_STEREO_32KHZ()147 const OpalAudioFormat & GetOpalL16_STEREO_32KHZ()
148 {
149   static OpalStereoAudioFormat stereo32k(OPAL_L16_STEREO_32KHZ,         // name of the media format
150 					RTP_DataFrame::L16_Stereo,      // RTP payload code
151 					"L16S",                         // encoding name
152 					128,                            // frame size in bytes
153 					32,                             // frame time (1 ms in clock units)
154 					240,                            // recommended rx frames/packet
155 					30,                             // recommended tx frames/packet
156 					256,                            // max tx frame size
157 					32000);                         // clock rate
158   return stereo32k;
159 };
160 
GetOpalPCM16S_48KHZ()161 const OpalAudioFormat & GetOpalPCM16S_48KHZ()
162 {
163   static OpalStereoAudioFormat stereo48k(OPAL_PCM16S_48KHZ,		// name of the media format
164 					RTP_DataFrame::MaxPayloadType,	// RTP payload code
165 					"",				// encoding name
166 					192,				// frame size in bytes
167 					48,				// frame time (1 ms in clock units)
168 					240,				// recommended rx frames/packet
169 					0,				// recommended tx frames/packet
170 					256,				// max tx frame size
171 					48000);				// clock rate
172   return stereo48k;
173 };
174 
GetOpalL16_STEREO_48KHZ()175 const OpalAudioFormat & GetOpalL16_STEREO_48KHZ()
176 {
177   static OpalStereoAudioFormat stereo48k(OPAL_L16_STEREO_48KHZ,         // name of the media format
178 					RTP_DataFrame::L16_Stereo,      // RTP payload code
179 					"L16S",                         // encoding name
180 					192,                            // frame size in bytes
181 					48,                             // frame time (1 ms in clock units)
182 					240,                            // recommended rx frames/packet
183 					30,                             // recommended tx frames/packet
184 					256,                            // max tx frame size
185 					48000);                         // clock rate
186   return stereo48k;
187 };
188 
189 
GetMediaFormatsList()190 static OpalMediaFormatList & GetMediaFormatsList()
191 {
192   static class OpalMediaFormatListMaster : public OpalMediaFormatList
193   {
194     public:
195       OpalMediaFormatListMaster()
196       {
197         DisallowDeleteObjects();
198       }
199   } registeredFormats;
200 
201   return registeredFormats;
202 }
203 
204 
GetMediaFormatsListMutex()205 static PMutex & GetMediaFormatsListMutex()
206 {
207   static PMutex mutex;
208   return mutex;
209 }
210 
211 
Clamp(OpalMediaFormatInternal & fmt1,const OpalMediaFormatInternal & fmt2,const PString & variableOption,const PString & minOption,const PString & maxOption)212 static void Clamp(OpalMediaFormatInternal & fmt1, const OpalMediaFormatInternal & fmt2, const PString & variableOption, const PString & minOption, const PString & maxOption)
213 {
214   if (fmt1.FindOption(variableOption) == NULL)
215     return;
216 
217   unsigned value    = fmt1.GetOptionInteger(variableOption, 0);
218   unsigned minValue = fmt2.GetOptionInteger(minOption, 0);
219   unsigned maxValue = fmt2.GetOptionInteger(maxOption, UINT_MAX);
220   if (value < minValue) {
221     PTRACE(4, "MediaFormat\tClamped media option \"" << variableOption << "\" from " << value << " to min " << minValue);
222     fmt1.SetOptionInteger(variableOption, minValue);
223   }
224   else if (value > maxValue) {
225     PTRACE(4, "MediaFormat\tClamped media option \"" << variableOption << "\" from " << value << " to max " << maxValue);
226     fmt1.SetOptionInteger(variableOption, maxValue);
227   }
228 }
229 
230 
231 /////////////////////////////////////////////////////////////////////////////
232 
OpalMediaOption(const PString & name)233 OpalMediaOption::OpalMediaOption(const PString & name)
234   : m_name(name)
235   , m_readOnly(false)
236   , m_merge(NoMerge)
237 {
238 }
239 
240 
OpalMediaOption(const char * name,bool readOnly,MergeType merge)241 OpalMediaOption::OpalMediaOption(const char * name, bool readOnly, MergeType merge)
242   : m_name(name)
243   , m_readOnly(readOnly)
244   , m_merge(merge)
245 {
246   m_name.Replace("=", "_", true);
247 }
248 
249 
Compare(const PObject & obj) const250 PObject::Comparison OpalMediaOption::Compare(const PObject & obj) const
251 {
252   const OpalMediaOption * otherOption = PDownCast(const OpalMediaOption, &obj);
253   if (otherOption == NULL)
254     return GreaterThan;
255   return m_name.Compare(otherOption->m_name);
256 }
257 
258 
Merge(const OpalMediaOption & option)259 bool OpalMediaOption::Merge(const OpalMediaOption & option)
260 {
261   bool assign;
262   switch (m_merge) {
263     case MinMerge :
264       assign = CompareValue(option) == GreaterThan;
265       break;
266 
267     case MaxMerge :
268       assign = CompareValue(option) == LessThan;
269       break;
270 
271     case EqualMerge :
272       if (CompareValue(option) == EqualTo)
273         return true;
274       PTRACE(2, "MediaFormat\tMerge of media option \"" << m_name << "\" failed, "
275                 "required to be equal: \"" << *this << "\"!=\"" << option << '"');
276       return false;
277 
278     case NotEqualMerge :
279       if (CompareValue(option) != EqualTo)
280         return true;
281       PTRACE(2, "MediaFormat\tMerge of media option \"" << m_name << "\" failed, "
282                 "required to be not equal: \"" << *this << "\"==\"" << option << '"');
283       return false;
284 
285     case AlwaysMerge :
286       assign = CompareValue(option) != EqualTo;
287       break;
288 
289     default :
290       assign = false;
291       break;
292   }
293 
294   if (assign) {
295     PTRACE(4, "MediaFormat\tChanged media option \"" << m_name << "\" "
296               "from \"" << *this << "\" to \"" << option << '"');
297     Assign(option);
298   }
299 
300   return true;
301 }
302 
303 
ValidateMerge(const OpalMediaOption & option) const304 bool OpalMediaOption::ValidateMerge(const OpalMediaOption & option) const
305 {
306   switch (m_merge) {
307     case EqualMerge :
308       if (CompareValue(option) == EqualTo)
309         return true;
310       break;
311 
312     case NotEqualMerge :
313       if (CompareValue(option) != EqualTo)
314         return true;
315       break;
316 
317     default :
318       return true;
319   }
320 
321   PTRACE(2, "MediaFormat\tValidation of merge for media option \"" << m_name << "\" failed.");
322   return false;
323 }
324 
325 
AsString() const326 PString OpalMediaOption::AsString() const
327 {
328   PStringStream strm;
329   PrintOn(strm);
330   return strm;
331 }
332 
333 
FromString(const PString & value)334 bool OpalMediaOption::FromString(const PString & value)
335 {
336   PStringStream strm;
337   strm = value;
338   ReadFrom(strm);
339   return !strm.fail();
340 }
341 
342 
343 ///////////////////////////////////////
344 
OpalMediaOptionEnum(const char * name,bool readOnly)345 OpalMediaOptionEnum::OpalMediaOptionEnum(const char * name, bool readOnly)
346   : OpalMediaOption(name, readOnly, EqualMerge)
347   , m_value(0)
348 {
349 }
350 
351 
OpalMediaOptionEnum(const char * name,bool readOnly,const char * const * enumerations,PINDEX count,MergeType merge,PINDEX value)352 OpalMediaOptionEnum::OpalMediaOptionEnum(const char * name,
353                                          bool readOnly,
354                                          const char * const * enumerations,
355                                          PINDEX count,
356                                          MergeType merge,
357                                          PINDEX value)
358   : OpalMediaOption(name, readOnly, merge),
359     m_enumerations(count, enumerations),
360     m_value(value)
361 {
362   if (m_value >= count)
363     m_value = count;
364 }
365 
366 
Clone() const367 PObject * OpalMediaOptionEnum::Clone() const
368 {
369   return new OpalMediaOptionEnum(*this);
370 }
371 
372 
PrintOn(ostream & strm) const373 void OpalMediaOptionEnum::PrintOn(ostream & strm) const
374 {
375   if (m_value < m_enumerations.GetSize())
376     strm << m_enumerations[m_value];
377   else
378     strm << psprintf("<%u>", m_value); // Don't output direct to stream so width() works correctly
379 }
380 
381 
ReadFrom(istream & strm)382 void OpalMediaOptionEnum::ReadFrom(istream & strm)
383 {
384   m_value = m_enumerations.GetSize();
385 
386   PINDEX longestMatch = 0;
387 
388   PCaselessString str;
389   while (strm.peek() != EOF) {
390     str += (char)strm.get();
391 
392     PINDEX i;
393     for (i = 0; i < m_enumerations.GetSize(); i++) {
394       if (str == m_enumerations[i].Left(str.GetLength())) {
395         longestMatch = i;
396         break;
397       }
398     }
399     if (i >= m_enumerations.GetSize()) {
400       i = str.GetLength()-1;
401       strm.putback(str[i]);
402       str.Delete(i, 1);
403       break;
404     }
405   }
406 
407   if (str == m_enumerations[longestMatch])
408     m_value = longestMatch;
409   else {
410     for (PINDEX i = str.GetLength(); i > 0; )
411       strm.putback(str[--i]);
412     strm.setstate(ios::failbit);
413   }
414 }
415 
416 
CompareValue(const OpalMediaOption & option) const417 PObject::Comparison OpalMediaOptionEnum::CompareValue(const OpalMediaOption & option) const
418 {
419   const OpalMediaOptionEnum * otherOption = PDownCast(const OpalMediaOptionEnum, &option);
420   if (otherOption == NULL)
421     return GreaterThan;
422 
423   if (m_value > otherOption->m_value)
424     return GreaterThan;
425 
426   if (m_value < otherOption->m_value)
427     return LessThan;
428 
429   return EqualTo;
430 }
431 
432 
Assign(const OpalMediaOption & option)433 void OpalMediaOptionEnum::Assign(const OpalMediaOption & option)
434 {
435   const OpalMediaOptionEnum * otherOption = PDownCast(const OpalMediaOptionEnum, &option);
436   if (otherOption != NULL)
437     m_value = otherOption->m_value;
438 }
439 
440 
SetValue(PINDEX value)441 void OpalMediaOptionEnum::SetValue(PINDEX value)
442 {
443   if (value < m_enumerations.GetSize())
444     m_value = value;
445   else {
446     m_value = m_enumerations.GetSize();
447     PTRACE(1, "MediaFormat\tIllegal value (" << value << ") for OpalMediaOptionEnum");
448   }
449 }
450 
451 
452 ///////////////////////////////////////
453 
OpalMediaOptionString(const char * name,bool readOnly)454 OpalMediaOptionString::OpalMediaOptionString(const char * name, bool readOnly)
455   : OpalMediaOption(name, readOnly, NoMerge)
456 {
457 }
458 
459 
OpalMediaOptionString(const char * name,bool readOnly,const PString & value)460 OpalMediaOptionString::OpalMediaOptionString(const char * name, bool readOnly, const PString & value)
461   : OpalMediaOption(name, readOnly, NoMerge),
462     m_value(value)
463 {
464 }
465 
466 
Clone() const467 PObject * OpalMediaOptionString::Clone() const
468 {
469   OpalMediaOptionString * newObj = new OpalMediaOptionString(*this);
470   newObj->m_value.MakeUnique();
471   return newObj;
472 }
473 
474 
PrintOn(ostream & strm) const475 void OpalMediaOptionString::PrintOn(ostream & strm) const
476 {
477   strm << m_value;
478 }
479 
480 
ReadFrom(istream & strm)481 void OpalMediaOptionString::ReadFrom(istream & strm)
482 {
483   while (isspace(strm.peek())) // Skip whitespace
484     strm.get();
485 
486   if (strm.peek() != '"')
487     strm >> m_value; // If no '"' then read to end of line or eof.
488   else {
489     // If there was a '"' then assume it is a C style literal string with \ escapes etc
490     // The following will set the bad bit if eof occurs before
491 
492     char c = ' ';
493     PINDEX count = 0;
494     PStringStream str;
495     while (strm.peek() != EOF) {
496       strm.get(c);
497       str << c;
498 
499       // Keep reading till get a '"' that is not preceded by a '\' that is not itself preceded by a '\'
500       if (c == '"' && count > 0 && (str[count] != '\\' || !(count > 1 && str[count-1] == '\\')))
501         break;
502 
503       count++;
504     }
505 
506     if (c != '"') {
507       // No closing quote, add one and set fail bit.
508       strm.setstate(ios::failbit);
509       str << '"';
510     }
511 
512     m_value = PString(PString::Literal, (const char *)str);
513   }
514 }
515 
516 
Merge(const OpalMediaOption & option)517 bool OpalMediaOptionString::Merge(const OpalMediaOption & option)
518 {
519   if (m_merge != IntersectionMerge)
520     return OpalMediaOption::Merge(option);
521 
522   const OpalMediaOptionString * otherOption = PDownCast(const OpalMediaOptionString, &option);
523   if (otherOption == NULL)
524     return false;
525 
526   PStringArray mySet = m_value.Tokenise(',');
527   PStringArray otherSet = otherOption->m_value.Tokenise(',');
528   PINDEX i = 0;
529   while (i < mySet.GetSize()) {
530     if (otherSet.GetValuesIndex(mySet[i]) != P_MAX_INDEX)
531       ++i;
532     else
533       mySet.RemoveAt(i);
534   }
535 
536   if (mySet.IsEmpty())
537     m_value.MakeEmpty();
538   else {
539     m_value = mySet[0];
540     for (i = 1; i < mySet.GetSize(); ++i)
541       m_value += ',' + mySet[i];
542   }
543 
544   return true;
545 }
546 
547 
CompareValue(const OpalMediaOption & option) const548 PObject::Comparison OpalMediaOptionString::CompareValue(const OpalMediaOption & option) const
549 {
550   const OpalMediaOptionString * otherOption = PDownCast(const OpalMediaOptionString, &option);
551   if (otherOption == NULL)
552     return GreaterThan;
553 
554   return m_value.Compare(otherOption->m_value);
555 }
556 
557 
Assign(const OpalMediaOption & option)558 void OpalMediaOptionString::Assign(const OpalMediaOption & option)
559 {
560   const OpalMediaOptionString * otherOption = PDownCast(const OpalMediaOptionString, &option);
561   if (otherOption != NULL) {
562     m_value = otherOption->m_value;
563     m_value.MakeUnique();
564   }
565 }
566 
567 
SetValue(const PString & value)568 void OpalMediaOptionString::SetValue(const PString & value)
569 {
570   m_value = value;
571   m_value.MakeUnique();
572 }
573 
574 
575 ///////////////////////////////////////
576 
OpalMediaOptionOctets(const char * name,bool readOnly,bool base64)577 OpalMediaOptionOctets::OpalMediaOptionOctets(const char * name, bool readOnly, bool base64)
578   : OpalMediaOption(name, readOnly, NoMerge)
579   , m_base64(base64)
580 {
581 }
582 
583 
OpalMediaOptionOctets(const char * name,bool readOnly,bool base64,const PBYTEArray & value)584 OpalMediaOptionOctets::OpalMediaOptionOctets(const char * name, bool readOnly, bool base64, const PBYTEArray & value)
585   : OpalMediaOption(name, readOnly, NoMerge)
586   , m_value(value)
587   , m_base64(base64)
588 {
589 }
590 
591 
OpalMediaOptionOctets(const char * name,bool readOnly,bool base64,const BYTE * data,PINDEX length)592 OpalMediaOptionOctets::OpalMediaOptionOctets(const char * name, bool readOnly, bool base64, const BYTE * data, PINDEX length)
593   : OpalMediaOption(name, readOnly, NoMerge)
594   , m_value(data, length)
595   , m_base64(base64)
596 {
597 }
598 
599 
Clone() const600 PObject * OpalMediaOptionOctets::Clone() const
601 {
602   OpalMediaOptionOctets * newObj = new OpalMediaOptionOctets(*this);
603   newObj->m_value.MakeUnique();
604   return newObj;
605 }
606 
607 
PrintOn(ostream & strm) const608 void OpalMediaOptionOctets::PrintOn(ostream & strm) const
609 {
610   if (m_base64)
611     strm << PBase64::Encode(m_value);
612   else {
613     streamsize width = strm.width();
614     ios::fmtflags flags = strm.flags();
615     char fill = strm.fill();
616 
617     streamsize fillLength = width - m_value.GetSize()*2;
618     if (fillLength > 0 && (flags&ios_base::adjustfield) == ios::right) {
619       for (streamsize i = 0; i < fillLength; i++)
620         strm << fill;
621     }
622 
623     strm << right << hex << setfill('0');
624     for (PINDEX i = 0; i < m_value.GetSize(); i++)
625       strm << setw(2) << (unsigned)m_value[i];
626 
627     if (fillLength > 0 && (flags&ios_base::adjustfield) == ios::left) {
628       strm << setw(1);
629       for (std::streamsize i = 0; i < fillLength; i++)
630         strm << fill;
631     }
632 
633     strm.fill(fill);
634     strm.flags(flags);
635   }
636 }
637 
638 
ReadFrom(istream & strm)639 void OpalMediaOptionOctets::ReadFrom(istream & strm)
640 {
641   if (m_base64) {
642     PString str;
643     strm >> str;
644     PBase64::Decode(str, m_value);
645   }
646   else {
647     char pair[3];
648     pair[2] = '\0';
649 
650     PINDEX count = 0;
651     PINDEX nibble = 0;
652 
653     while (strm.peek() != EOF) {
654       char ch = (char)strm.get();
655       if (isxdigit(ch))
656         pair[nibble++] = ch;
657       else if (ch == ' ')
658         pair[nibble++] = '0';
659       else
660         break;
661 
662       if (nibble == 2) {
663         if (!m_value.SetMinSize(100*((count+1+99)/100)))
664           break;
665         m_value[count++] = (BYTE)strtoul(pair, NULL, 16);
666         nibble = 0;
667       }
668     }
669 
670     // Report error if no legal hex, not empty is OK.
671     if (count == 0 && !strm.eof())
672       strm.setstate(ios::failbit);
673 
674     m_value.SetSize(count);
675   }
676 }
677 
678 
CompareValue(const OpalMediaOption & option) const679 PObject::Comparison OpalMediaOptionOctets::CompareValue(const OpalMediaOption & option) const
680 {
681   const OpalMediaOptionOctets * otherOption = PDownCast(const OpalMediaOptionOctets, &option);
682   if (otherOption == NULL)
683     return GreaterThan;
684 
685   return m_value.Compare(otherOption->m_value);
686 }
687 
688 
Assign(const OpalMediaOption & option)689 void OpalMediaOptionOctets::Assign(const OpalMediaOption & option)
690 {
691   const OpalMediaOptionOctets * otherOption = PDownCast(const OpalMediaOptionOctets, &option);
692   if (otherOption != NULL) {
693     m_value = otherOption->m_value;
694     m_value.MakeUnique();
695   }
696 }
697 
698 
SetValue(const PBYTEArray & value)699 void OpalMediaOptionOctets::SetValue(const PBYTEArray & value)
700 {
701   m_value = value;
702   m_value.MakeUnique();
703 }
704 
705 
SetValue(const BYTE * data,PINDEX length)706 void OpalMediaOptionOctets::SetValue(const BYTE * data, PINDEX length)
707 {
708   m_value = PBYTEArray(data, length);
709 }
710 
711 
712 /////////////////////////////////////////////////////////////////////////////
713 
NeedsJitterOption()714 const PString & OpalMediaFormat::NeedsJitterOption()   { static const PConstString s(PLUGINCODEC_OPTION_NEEDS_JITTER);    return s; }
MaxFrameSizeOption()715 const PString & OpalMediaFormat::MaxFrameSizeOption()  { static const PConstString s(PLUGINCODEC_OPTION_MAX_FRAME_SIZE);  return s; }
FrameTimeOption()716 const PString & OpalMediaFormat::FrameTimeOption()     { static const PConstString s(PLUGINCODEC_OPTION_FRAME_TIME);      return s; }
ClockRateOption()717 const PString & OpalMediaFormat::ClockRateOption()     { static const PConstString s(PLUGINCODEC_OPTION_CLOCK_RATE);      return s; }
MaxBitRateOption()718 const PString & OpalMediaFormat::MaxBitRateOption()    { static const PConstString s(PLUGINCODEC_OPTION_MAX_BIT_RATE);    return s; }
TargetBitRateOption()719 const PString & OpalMediaFormat::TargetBitRateOption() { static const PConstString s(PLUGINCODEC_OPTION_TARGET_BIT_RATE); return s; }
720 
721 #if OPAL_H323
MediaPacketizationOption()722 const PString & OpalMediaFormat::MediaPacketizationOption()  { static const PConstString s(PLUGINCODEC_MEDIA_PACKETIZATION);  return s; }
MediaPacketizationsOption()723 const PString & OpalMediaFormat::MediaPacketizationsOption() { static const PConstString s(PLUGINCODEC_MEDIA_PACKETIZATIONS); return s; }
724 #endif
725 
ProtocolOption()726 const PString & OpalMediaFormat::ProtocolOption()        { static const PConstString s(PLUGINCODEC_OPTION_PROTOCOL); return s; }
MaxTxPacketSizeOption()727 const PString & OpalMediaFormat::MaxTxPacketSizeOption() { static const PConstString s(PLUGINCODEC_OPTION_MAX_TX_PACKET_SIZE); return s; }
728 
OpalMediaFormat(OpalMediaFormatInternal * info)729 OpalMediaFormat::OpalMediaFormat(OpalMediaFormatInternal * info)
730   : m_info(NULL)
731 {
732   Construct(info);
733 }
734 
735 
OpalMediaFormat(RTP_DataFrame::PayloadTypes pt,unsigned clockRate,const char * name,const char * protocol)736 OpalMediaFormat::OpalMediaFormat(RTP_DataFrame::PayloadTypes pt, unsigned clockRate, const char * name, const char * protocol)
737   : m_info(NULL)
738 {
739   PWaitAndSignal mutex(GetMediaFormatsListMutex());
740   const OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
741 
742   OpalMediaFormatList::const_iterator fmt = registeredFormats.FindFormat(pt, clockRate, name, protocol);
743   if (fmt != registeredFormats.end())
744     *this = *fmt;
745 }
746 
747 
OpalMediaFormat(const char * wildcard)748 OpalMediaFormat::OpalMediaFormat(const char * wildcard)
749   : m_info(NULL)
750 {
751   operator=(PString(wildcard));
752 }
753 
754 
OpalMediaFormat(const PString & wildcard)755 OpalMediaFormat::OpalMediaFormat(const PString & wildcard)
756   : m_info(NULL)
757 {
758   operator=(wildcard);
759 }
760 
761 
OpalMediaFormat(const char * fullName,const OpalMediaType & mediaType,RTP_DataFrame::PayloadTypes pt,const char * en,PBoolean nj,unsigned bw,PINDEX fs,unsigned ft,unsigned cr,time_t ts)762 OpalMediaFormat::OpalMediaFormat(const char * fullName,
763                                  const OpalMediaType & mediaType,
764                                  RTP_DataFrame::PayloadTypes pt,
765                                  const char * en,
766                                  PBoolean     nj,
767                                  unsigned bw,
768                                  PINDEX   fs,
769                                  unsigned ft,
770                                  unsigned cr,
771                                  time_t ts)
772 {
773   Construct(new OpalMediaFormatInternal(fullName, mediaType, pt, en, nj, bw, fs, ft,cr, ts));
774 }
775 
776 
OpalMediaFormat(const OpalMediaFormat & c)777 OpalMediaFormat::OpalMediaFormat(const OpalMediaFormat & c)
778   : PContainer() // can't use PContainer copy c-tor as this c-tor must be synchronized
779   , m_info(NULL)
780 {
781   PWaitAndSignal m(c.m_mutex); // here is no need to use mutex of the shared object
782   PContainer::AssignContents(c);
783   m_info = c.m_info;
784 }
785 
786 
~OpalMediaFormat()787 OpalMediaFormat::~OpalMediaFormat()
788 {
789   if (m_info != NULL)
790     m_info->media_format_mutex.Wait(); // don't use PWaitAndSignal as m_info can be removed
791 
792   Destruct();
793 
794   if (m_info != NULL)
795     m_info->media_format_mutex.Signal();
796 }
797 
798 
Construct(OpalMediaFormatInternal * info)799 void OpalMediaFormat::Construct(OpalMediaFormatInternal * info)
800 {
801   if (info == NULL)
802     return;
803 
804   PWaitAndSignal mutex(GetMediaFormatsListMutex());
805   OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
806 
807   OpalMediaFormatList::const_iterator fmt = registeredFormats.FindFormat(info->formatName);
808   if (fmt != registeredFormats.end()) {
809     *this = *fmt;
810     delete info;
811   }
812   else {
813     m_info = info;
814     registeredFormats.OpalMediaFormatBaseList::Append(this);
815   }
816 }
817 
818 
operator =(RTP_DataFrame::PayloadTypes pt)819 OpalMediaFormat & OpalMediaFormat::operator=(RTP_DataFrame::PayloadTypes pt)
820 {
821   PWaitAndSignal m(m_mutex);
822 
823   PWaitAndSignal mutex(GetMediaFormatsListMutex());
824   const OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
825 
826   OpalMediaFormatList::const_iterator fmt = registeredFormats.FindFormat(pt);
827   if (fmt == registeredFormats.end())
828     *this = OpalMediaFormat();
829   else if (this != &*fmt)
830     *this = *fmt;
831 
832   return *this;
833 }
834 
835 
operator =(const char * wildcard)836 OpalMediaFormat & OpalMediaFormat::operator=(const char * wildcard)
837 {
838   PWaitAndSignal m(m_mutex);
839   return operator=(PString(wildcard));
840 }
841 
842 
operator =(const PString & wildcard)843 OpalMediaFormat & OpalMediaFormat::operator=(const PString & wildcard)
844 {
845   PWaitAndSignal m(m_mutex);
846   PWaitAndSignal mutex(GetMediaFormatsListMutex());
847   const OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
848 
849   OpalMediaFormatList::const_iterator fmt = registeredFormats.FindFormat(wildcard);
850   if (fmt == registeredFormats.end())
851     *this = OpalMediaFormat();
852   else
853     *this = *fmt;
854 
855   return *this;
856 }
857 
858 
MakeUnique()859 PBoolean OpalMediaFormat::MakeUnique()
860 {
861   PWaitAndSignal m1(m_mutex);
862   if (m_info == NULL)
863     return true;
864 
865   PWaitAndSignal m2(m_info->media_format_mutex);
866 
867   if (PContainer::MakeUnique())
868     return true;
869 
870   m_info = (OpalMediaFormatInternal *)m_info->Clone();
871   m_info->options.MakeUnique();
872   return false;
873 }
874 
875 
AssignContents(const PContainer & c)876 void OpalMediaFormat::AssignContents(const PContainer & c)
877 {
878   PWaitAndSignal m1(m_mutex);
879 
880   const OpalMediaFormat & other = (const OpalMediaFormat &)c;
881   PWaitAndSignal m2(other.m_mutex);
882 
883   if (m_info != NULL) {
884     m_info->media_format_mutex.Wait(); // don't use PWaitAndSignal as m_info can be removed
885     PContainer::AssignContents(c);
886     if (m_info != NULL)
887       m_info->media_format_mutex.Signal();
888   }
889   else // current object can't be removed
890     PContainer::AssignContents(c);
891 
892   m_info = other.m_info;
893 }
894 
895 
DestroyContents()896 void OpalMediaFormat::DestroyContents()
897 {
898   m_mutex.Wait();
899 
900   if (m_info != NULL) {
901     delete m_info;
902     m_info = NULL;
903   }
904 
905   m_mutex.Signal();
906 }
907 
908 
Clone() const909 PObject * OpalMediaFormat::Clone() const
910 {
911   return new OpalMediaFormat(*this);
912 }
913 
914 
Compare(const PObject & obj) const915 PObject::Comparison OpalMediaFormat::Compare(const PObject & obj) const
916 {
917   PWaitAndSignal m(m_mutex);
918   PAssert(PIsDescendant(&obj, OpalMediaFormat), PInvalidCast);
919   const OpalMediaFormat & other = (const OpalMediaFormat &)obj;
920   if (m_info == NULL)
921     return other.m_info == NULL ? EqualTo : LessThan;
922   if (other.m_info == NULL)
923     return m_info == NULL ? EqualTo : GreaterThan;
924   return m_info->formatName.Compare(other.m_info->formatName);
925 }
926 
927 
PrintOn(ostream & strm) const928 void OpalMediaFormat::PrintOn(ostream & strm) const
929 {
930   PWaitAndSignal m(m_mutex);
931   if (m_info != NULL)
932     strm << *m_info;
933 }
934 
935 
ReadFrom(istream & strm)936 void OpalMediaFormat::ReadFrom(istream & strm)
937 {
938   PWaitAndSignal m(m_mutex);
939   char fmt[100];
940   strm >> fmt;
941   operator=(fmt);
942 }
943 
944 
ToNormalisedOptions()945 bool OpalMediaFormat::ToNormalisedOptions()
946 {
947   PWaitAndSignal m(m_mutex);
948   MakeUnique();
949   return m_info != NULL && m_info->ToNormalisedOptions();
950 }
951 
952 
ToCustomisedOptions()953 bool OpalMediaFormat::ToCustomisedOptions()
954 {
955   PWaitAndSignal m(m_mutex);
956   MakeUnique();
957   return m_info != NULL && m_info->ToCustomisedOptions();
958 }
959 
960 
Update(const OpalMediaFormat & mediaFormat)961 bool OpalMediaFormat::Update(const OpalMediaFormat & mediaFormat)
962 {
963   if (!mediaFormat.IsValid())
964     return true;
965 
966   PWaitAndSignal m(m_mutex);
967   MakeUnique();
968 
969   if (*this != mediaFormat)
970     return Merge(mediaFormat);
971 
972   if (!IsValid() || !Merge(mediaFormat))
973     *this = mediaFormat; //Must have different EqualMerge options, just copy it
974   else if (GetPayloadType() != mediaFormat.GetPayloadType()) {
975     PTRACE(4, "MediaFormat\tChanging payload type from " << GetPayloadType()
976            << " to " << mediaFormat.GetPayloadType() << " in " << *this);
977     SetPayloadType(mediaFormat.GetPayloadType());
978   }
979 
980   return true;
981 }
982 
983 
Merge(const OpalMediaFormat & mediaFormat)984 bool OpalMediaFormat::Merge(const OpalMediaFormat & mediaFormat)
985 {
986   PWaitAndSignal m(m_mutex);
987   MakeUnique();
988   return m_info != NULL && mediaFormat.m_info != NULL && m_info->Merge(*mediaFormat.m_info);
989 }
990 
991 
ValidateMerge(const OpalMediaFormat & mediaFormat) const992 bool OpalMediaFormat::ValidateMerge(const OpalMediaFormat & mediaFormat) const
993 {
994   PWaitAndSignal m(m_mutex);
995   return m_info != NULL && mediaFormat.m_info != NULL && m_info->ValidateMerge(*mediaFormat.m_info);
996 }
997 
998 
999 #if OPAL_H323
GetMediaPacketizations() const1000 PStringSet OpalMediaFormat::GetMediaPacketizations() const
1001 {
1002   return GetOptionString(OpalMediaFormat::MediaPacketizationsOption(),
1003                          GetOptionString(OpalMediaFormat::MediaPacketizationOption())).Tokenise(",");
1004 }
1005 
1006 
SetMediaPacketizations(const PStringSet & packetizations)1007 void OpalMediaFormat::SetMediaPacketizations(const PStringSet & packetizations)
1008 {
1009   if (packetizations.IsEmpty()) {
1010     SetOptionString(MediaPacketizationsOption(), PString::Empty());
1011     SetOptionString(MediaPacketizationOption(),  PString::Empty());
1012   }
1013   else {
1014     PStringStream strm;
1015     strm << setfill(',') << packetizations;
1016     SetOptionString(MediaPacketizationsOption(), strm);
1017     SetOptionString(MediaPacketizationOption(),  packetizations.GetKeyAt(0));
1018   }
1019 }
1020 #endif
1021 
1022 
GetAllRegisteredMediaFormats()1023 OpalMediaFormatList OpalMediaFormat::GetAllRegisteredMediaFormats()
1024 {
1025   OpalMediaFormatList copy;
1026   GetAllRegisteredMediaFormats(copy);
1027   return copy;
1028 }
1029 
1030 
GetAllRegisteredMediaFormats(OpalMediaFormatList & copy)1031 void OpalMediaFormat::GetAllRegisteredMediaFormats(OpalMediaFormatList & copy)
1032 {
1033   PWaitAndSignal mutex(GetMediaFormatsListMutex());
1034   const OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
1035 
1036   for (OpalMediaFormatList::const_iterator format = registeredFormats.begin(); format != registeredFormats.end(); ++format)
1037     copy += *format;
1038 }
1039 
1040 
SetRegisteredMediaFormat(const OpalMediaFormat & mediaFormat)1041 bool OpalMediaFormat::SetRegisteredMediaFormat(const OpalMediaFormat & mediaFormat)
1042 {
1043   PWaitAndSignal mutex(GetMediaFormatsListMutex());
1044   OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
1045 
1046   for (OpalMediaFormatList::iterator format = registeredFormats.begin(); format != registeredFormats.end(); ++format) {
1047     if (*format == mediaFormat) {
1048       /* Yes, this looks a little odd as we just did equality above and seem to
1049          be assigning the left hand side with exactly the same value. But what
1050          is really happening is the above only compares the name, and below
1051          copies all of the attributes (OpalMediaFormatOtions) across. */
1052       *format = mediaFormat;
1053       return true;
1054     }
1055   }
1056 
1057   return false;
1058 }
1059 
1060 
RemoveRegisteredMediaFormat(const OpalMediaFormat & mediaFormat)1061 bool OpalMediaFormat::RemoveRegisteredMediaFormat(const OpalMediaFormat & mediaFormat)
1062 {
1063   PWaitAndSignal mutex(GetMediaFormatsListMutex());
1064   OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
1065 
1066   for (OpalMediaFormatList::iterator format = registeredFormats.begin(); format != registeredFormats.end(); ++format) {
1067     if (*format == mediaFormat) {
1068       registeredFormats.erase(format);
1069       return true;
1070     }
1071   }
1072 
1073   return false;
1074 }
1075 
1076 
1077 /////////////////////////////////////////////////////////////////////////////
1078 
OpalMediaFormatInternal(const char * fullName,const OpalMediaType & _mediaType,RTP_DataFrame::PayloadTypes pt,const char * en,PBoolean nj,unsigned bw,PINDEX fs,unsigned ft,unsigned cr,time_t ts)1079 OpalMediaFormatInternal::OpalMediaFormatInternal(const char * fullName,
1080                                                  const OpalMediaType & _mediaType,
1081                                                  RTP_DataFrame::PayloadTypes pt,
1082                                                  const char * en,
1083                                                  PBoolean     nj,
1084                                                  unsigned bw,
1085                                                  PINDEX   fs,
1086                                                  unsigned ft,
1087                                                  unsigned cr,
1088                                                  time_t   ts)
1089   : formatName(fullName), mediaType(_mediaType), forceIsTransportable(false)
1090 {
1091   codecVersionTime = ts;
1092   rtpPayloadType   = pt;
1093   rtpEncodingName  = en;
1094   m_channels       = 1;    // this is the default - it's up to descendant classes to change it
1095 
1096   if (nj)
1097     AddOption(new OpalMediaOptionBoolean(OpalMediaFormat::NeedsJitterOption(), true, OpalMediaOption::OrMerge, true));
1098 
1099   if (bw > 0)
1100     AddOption(new OpalMediaOptionUnsigned(OpalMediaFormat::MaxBitRateOption(), true, OpalMediaOption::MinMerge, bw, 100));
1101 
1102   if (fs > 0)
1103     AddOption(new OpalMediaOptionUnsigned(OpalMediaFormat::MaxFrameSizeOption(), true, OpalMediaOption::NoMerge, fs));
1104 
1105   if (ft > 0)
1106     AddOption(new OpalMediaOptionUnsigned(OpalMediaFormat::FrameTimeOption(), true, OpalMediaOption::NoMerge, ft));
1107 
1108   if (cr > 0)
1109     AddOption(new OpalMediaOptionUnsigned(OpalMediaFormat::ClockRateOption(), true, OpalMediaOption::NoMerge, cr));
1110 
1111   AddOption(new OpalMediaOptionString(OpalMediaFormat::ProtocolOption(), true));
1112 
1113   // assume non-dynamic payload types are correct and do not need deconflicting
1114   if (rtpPayloadType < RTP_DataFrame::DynamicBase || rtpPayloadType >= RTP_DataFrame::MaxPayloadType) {
1115     if (rtpPayloadType == RTP_DataFrame::MaxPayloadType &&
1116         rtpEncodingName.GetLength() > 0 &&
1117         rtpEncodingName[0] == '+') {
1118       forceIsTransportable = true;
1119       rtpEncodingName = rtpEncodingName.Mid(1);
1120     }
1121     return;
1122   }
1123 
1124   PWaitAndSignal mutex(GetMediaFormatsListMutex());
1125   OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
1126 
1127   // Search for conflicting RTP Payload Type, collecting in use payload types along the way
1128   bool inUse[RTP_DataFrame::MaxPayloadType];
1129   memset(inUse, 0, sizeof(inUse));
1130 
1131   OpalMediaFormat * match = NULL;
1132   for (OpalMediaFormatList::iterator format = registeredFormats.begin(); format != registeredFormats.end(); ++format) {
1133     RTP_DataFrame::PayloadTypes thisPayloadType = format->GetPayloadType();
1134     if (thisPayloadType == rtpPayloadType)
1135       match = &*format;
1136     if (thisPayloadType < RTP_DataFrame::MaxPayloadType)
1137       inUse[thisPayloadType] = true;
1138   }
1139 
1140   if (match == NULL)
1141     return; // No conflict
1142 
1143   // Determine next unused payload type, if all the dynamic ones are allocated then
1144   // we start downward toward the well known values.
1145   int nextUnused = RTP_DataFrame::DynamicBase;
1146   while (inUse[nextUnused]) {
1147     if (nextUnused < RTP_DataFrame::DynamicBase)
1148       --nextUnused;
1149     else if (++nextUnused >= RTP_DataFrame::MaxPayloadType)
1150       nextUnused = RTP_DataFrame::DynamicBase-1;
1151   }
1152 
1153   match->SetPayloadType((RTP_DataFrame::PayloadTypes)nextUnused);
1154 }
1155 
1156 
Clone() const1157 PObject * OpalMediaFormatInternal::Clone() const
1158 {
1159   PWaitAndSignal m1(media_format_mutex);
1160   return new OpalMediaFormatInternal(*this);
1161 }
1162 
1163 
Merge(const OpalMediaFormatInternal & mediaFormat)1164 bool OpalMediaFormatInternal::Merge(const OpalMediaFormatInternal & mediaFormat)
1165 {
1166   PTRACE(4, "MediaFormat\tMerging " << mediaFormat << " into " << *this);
1167 
1168   PWaitAndSignal m1(media_format_mutex);
1169   PWaitAndSignal m2(mediaFormat.media_format_mutex);
1170 
1171   for (PINDEX i = 0; i < options.GetSize(); i++) {
1172     OpalMediaOption & opt = options[i];
1173     PString name = opt.GetName();
1174     OpalMediaOption * option = mediaFormat.FindOption(opt.GetName());
1175     if (option == NULL) {
1176       PTRACE_IF(2, formatName == mediaFormat.formatName, "MediaFormat\tCannot merge unmatched option " << opt.GetName());
1177     }
1178     else
1179     {
1180       PAssert(option->GetName() == opt.GetName(), "find returned bad name");
1181       if (!opt.Merge(*option))
1182         return false;
1183     }
1184   }
1185 
1186   return true;
1187 }
1188 
1189 
ValidateMerge(const OpalMediaFormatInternal & mediaFormat) const1190 bool OpalMediaFormatInternal::ValidateMerge(const OpalMediaFormatInternal & mediaFormat) const
1191 {
1192   PWaitAndSignal m1(media_format_mutex);
1193   PWaitAndSignal m2(mediaFormat.media_format_mutex);
1194 
1195   for (PINDEX i = 0; i < options.GetSize(); i++) {
1196     OpalMediaOption & opt = options[i];
1197     PString name = opt.GetName();
1198     OpalMediaOption * option = mediaFormat.FindOption(opt.GetName());
1199     if (option == NULL) {
1200       PTRACE_IF(2, formatName == mediaFormat.formatName, "MediaFormat\tValidate: unmatched option " << opt.GetName());
1201     }
1202     else {
1203       PAssert(option->GetName() == opt.GetName(), "find returned bad name");
1204       if (!opt.ValidateMerge(*option))
1205         return false;
1206     }
1207   }
1208 
1209   return true;
1210 }
1211 
1212 
ToNormalisedOptions()1213 bool OpalMediaFormatInternal::ToNormalisedOptions()
1214 {
1215   return true;
1216 }
1217 
1218 
ToCustomisedOptions()1219 bool OpalMediaFormatInternal::ToCustomisedOptions()
1220 {
1221   return true;
1222 }
1223 
1224 
IsValid() const1225 bool OpalMediaFormatInternal::IsValid() const
1226 {
1227   return rtpPayloadType < RTP_DataFrame::IllegalPayloadType && !formatName.IsEmpty();
1228 }
1229 
1230 
IsTransportable() const1231 bool OpalMediaFormatInternal::IsTransportable() const
1232 {
1233   if (forceIsTransportable)
1234     return true;
1235 
1236   if (rtpPayloadType >= RTP_DataFrame::MaxPayloadType)
1237     return false;
1238 
1239   if (rtpPayloadType < RTP_DataFrame::LastKnownPayloadType)
1240     return true;
1241 
1242   return !rtpEncodingName.IsEmpty();
1243 }
1244 
1245 
GetOptions() const1246 PStringToString OpalMediaFormatInternal::GetOptions() const
1247 {
1248   PWaitAndSignal m1(media_format_mutex);
1249   PStringToString dict;
1250   for (PINDEX i = 0; i < options.GetSize(); i++)
1251     dict.SetAt(options[i].GetName(), options[i].AsString());
1252   return dict;
1253 }
1254 
1255 
GetOptionValue(const PString & name,PString & value) const1256 bool OpalMediaFormatInternal::GetOptionValue(const PString & name, PString & value) const
1257 {
1258   PWaitAndSignal m(media_format_mutex);
1259   OpalMediaOption * option = FindOption(name);
1260   if (option == NULL)
1261     return false;
1262 
1263   value = option->AsString();
1264   return true;
1265 }
1266 
1267 
SetOptionValue(const PString & name,const PString & value)1268 bool OpalMediaFormatInternal::SetOptionValue(const PString & name, const PString & value)
1269 {
1270   PWaitAndSignal m(media_format_mutex);
1271 
1272   OpalMediaOption * option = FindOption(name);
1273   if (option == NULL)
1274     return false;
1275 
1276   return option->FromString(value);
1277 }
1278 
1279 
1280 template <class OptionType, typename ValueType>
GetOptionOfType(const OpalMediaFormatInternal & format,const PString & name,ValueType dflt)1281 static ValueType GetOptionOfType(const OpalMediaFormatInternal & format, const PString & name, ValueType dflt)
1282 {
1283   OpalMediaOption * option = format.FindOption(name);
1284   if (option == NULL)
1285     return dflt;
1286 
1287   OptionType * typedOption = dynamic_cast<OptionType *>(option);
1288   if (typedOption != NULL)
1289     return typedOption->GetValue();
1290 
1291   PTRACE(1, "MediaFormat\tInvalid type for getting option " << name << " in " << format);
1292   PAssertAlways(PInvalidCast);
1293   return dflt;
1294 }
1295 
1296 
1297 template <class OptionType, typename ValueType>
SetOptionOfType(OpalMediaFormatInternal & format,const PString & name,ValueType value)1298 static bool SetOptionOfType(OpalMediaFormatInternal & format, const PString & name, ValueType value)
1299 {
1300   OpalMediaOption * option = format.FindOption(name);
1301   if (option == NULL)
1302     return false;
1303 
1304   OptionType * typedOption = dynamic_cast<OptionType *>(option);
1305   if (typedOption != NULL) {
1306     typedOption->SetValue(value);
1307     return true;
1308   }
1309 
1310   PTRACE(1, "MediaFormat\tInvalid type for setting option " << name << " in " << format);
1311   PAssertAlways(PInvalidCast);
1312   return false;
1313 }
1314 
1315 
GetOptionBoolean(const PString & name,bool dflt) const1316 bool OpalMediaFormatInternal::GetOptionBoolean(const PString & name, bool dflt) const
1317 {
1318   PWaitAndSignal m(media_format_mutex);
1319   const OpalMediaOptionEnum * optEnum = dynamic_cast<const OpalMediaOptionEnum *>(FindOption(name));
1320   if (optEnum != NULL && optEnum->GetEnumerations().GetSize() == 2)
1321     return optEnum->GetValue() != 0;
1322 
1323   return GetOptionOfType<OpalMediaOptionBoolean, bool>(*this, name, dflt);
1324 }
1325 
1326 
SetOptionBoolean(const PString & name,bool value)1327 bool OpalMediaFormatInternal::SetOptionBoolean(const PString & name, bool value)
1328 {
1329   PWaitAndSignal m(media_format_mutex);
1330   OpalMediaOptionEnum * optEnum = dynamic_cast<OpalMediaOptionEnum *>(FindOption(name));
1331   if (optEnum != NULL && optEnum->GetEnumerations().GetSize() == 2) {
1332     optEnum->SetValue(value);
1333     return true;
1334   }
1335 
1336   return SetOptionOfType<OpalMediaOptionBoolean, bool>(*this, name, value);
1337 }
1338 
1339 
GetOptionInteger(const PString & name,int dflt) const1340 int OpalMediaFormatInternal::GetOptionInteger(const PString & name, int dflt) const
1341 {
1342   PWaitAndSignal m(media_format_mutex);
1343   OpalMediaOptionUnsigned * optUnsigned = dynamic_cast<OpalMediaOptionUnsigned *>(FindOption(name));
1344   if (optUnsigned != NULL)
1345     return optUnsigned->GetValue();
1346 
1347   return GetOptionOfType<OpalMediaOptionInteger, int>(*this, name, dflt);
1348 }
1349 
1350 
SetOptionInteger(const PString & name,int value)1351 bool OpalMediaFormatInternal::SetOptionInteger(const PString & name, int value)
1352 {
1353   PWaitAndSignal m(media_format_mutex);
1354   OpalMediaOptionUnsigned * optUnsigned = dynamic_cast<OpalMediaOptionUnsigned *>(FindOption(name));
1355   if (optUnsigned != NULL) {
1356     optUnsigned->SetValue(value);
1357     return true;
1358   }
1359 
1360   return SetOptionOfType<OpalMediaOptionInteger, int>(*this, name, value);
1361 }
1362 
1363 
GetOptionReal(const PString & name,double dflt) const1364 double OpalMediaFormatInternal::GetOptionReal(const PString & name, double dflt) const
1365 {
1366   PWaitAndSignal m(media_format_mutex);
1367   return GetOptionOfType<OpalMediaOptionReal, double>(*this, name, dflt);
1368 }
1369 
1370 
SetOptionReal(const PString & name,double value)1371 bool OpalMediaFormatInternal::SetOptionReal(const PString & name, double value)
1372 {
1373   PWaitAndSignal m(media_format_mutex);
1374   return SetOptionOfType<OpalMediaOptionReal, double>(*this, name, value);
1375 }
1376 
1377 
GetOptionEnum(const PString & name,PINDEX dflt) const1378 PINDEX OpalMediaFormatInternal::GetOptionEnum(const PString & name, PINDEX dflt) const
1379 {
1380   PWaitAndSignal m(media_format_mutex);
1381   return GetOptionOfType<OpalMediaOptionEnum, PINDEX>(*this, name, dflt);
1382 }
1383 
1384 
SetOptionEnum(const PString & name,PINDEX value)1385 bool OpalMediaFormatInternal::SetOptionEnum(const PString & name, PINDEX value)
1386 {
1387   PWaitAndSignal m(media_format_mutex);
1388   return SetOptionOfType<OpalMediaOptionEnum, PINDEX>(*this, name, value);
1389 }
1390 
1391 
GetOptionString(const PString & name,const PString & dflt) const1392 PString OpalMediaFormatInternal::GetOptionString(const PString & name, const PString & dflt) const
1393 {
1394   PWaitAndSignal m(media_format_mutex);
1395   return GetOptionOfType<OpalMediaOptionString, PString>(*this, name, dflt);
1396 }
1397 
1398 
SetOptionString(const PString & name,const PString & value)1399 bool OpalMediaFormatInternal::SetOptionString(const PString & name, const PString & value)
1400 {
1401   PWaitAndSignal m(media_format_mutex);
1402   return SetOptionOfType<OpalMediaOptionString, PString>(*this, name, value);
1403 }
1404 
1405 
GetOptionOctets(const PString & name,PBYTEArray & octets) const1406 bool OpalMediaFormatInternal::GetOptionOctets(const PString & name, PBYTEArray & octets) const
1407 {
1408   PWaitAndSignal m(media_format_mutex);
1409   OpalMediaOption * option = FindOption(name);
1410   if (option == NULL)
1411     return false;
1412 
1413   octets = PDownCast(OpalMediaOptionOctets, option)->GetValue();
1414   return true;
1415 }
1416 
1417 
SetOptionOctets(const PString & name,const PBYTEArray & octets)1418 bool OpalMediaFormatInternal::SetOptionOctets(const PString & name, const PBYTEArray & octets)
1419 {
1420   PWaitAndSignal m(media_format_mutex);
1421   return SetOptionOfType<OpalMediaOptionOctets, const PBYTEArray &>(*this, name, octets);
1422 }
1423 
1424 
SetOptionOctets(const PString & name,const BYTE * data,PINDEX length)1425 bool OpalMediaFormatInternal::SetOptionOctets(const PString & name, const BYTE * data, PINDEX length)
1426 {
1427   PWaitAndSignal m(media_format_mutex);
1428   return SetOptionOfType<OpalMediaOptionOctets, const PBYTEArray &>(*this, name, PBYTEArray(data, length));
1429 }
1430 
1431 
AddOption(OpalMediaOption * option,PBoolean overwrite)1432 bool OpalMediaFormatInternal::AddOption(OpalMediaOption * option, PBoolean overwrite)
1433 {
1434   PWaitAndSignal m(media_format_mutex);
1435   if (PAssertNULL(option) == NULL)
1436     return false;
1437 
1438   PINDEX index = options.GetValuesIndex(*option);
1439   if (index != P_MAX_INDEX) {
1440     if (!overwrite) {
1441       delete option;
1442       return false;
1443     }
1444 
1445     options.RemoveAt(index);
1446   }
1447 
1448   options.Append(option);
1449   return true;
1450 }
1451 
1452 
1453 class OpalMediaOptionSearchArg : public OpalMediaOption
1454 {
1455 public:
OpalMediaOptionSearchArg(const PString & name)1456   OpalMediaOptionSearchArg(const PString & name) : OpalMediaOption(name) { }
CompareValue(const OpalMediaOption &) const1457   virtual Comparison CompareValue(const OpalMediaOption &) const { return EqualTo; }
Assign(const OpalMediaOption &)1458   virtual void Assign(const OpalMediaOption &) { }
1459 };
1460 
FindOption(const PString & name) const1461 OpalMediaOption * OpalMediaFormatInternal::FindOption(const PString & name) const
1462 {
1463   PWaitAndSignal m(media_format_mutex);
1464   OpalMediaOptionSearchArg search(name);
1465   PINDEX index = options.GetValuesIndex(search);
1466   if (index == P_MAX_INDEX)
1467     return NULL;
1468 
1469   PAssert(options[index].GetName() == name, "OpalMediaOption name mismatch");
1470 
1471   return &options[index];
1472 }
1473 
1474 
IsValidForProtocol(const PString & protocol) const1475 bool OpalMediaFormatInternal::IsValidForProtocol(const PString & protocol) const
1476 {
1477   PWaitAndSignal m(media_format_mutex);
1478 
1479   // the protocol is only valid for SIP if the RTP name is not NULL
1480   if (protocol *= "sip")
1481     return (rtpEncodingName != NULL) || (forceIsTransportable);
1482 
1483   return true;
1484 }
1485 
1486 
PrintOn(ostream & strm) const1487 void OpalMediaFormatInternal::PrintOn(ostream & strm) const
1488 {
1489   PINDEX i;
1490   PWaitAndSignal m(media_format_mutex);
1491 
1492   if (strm.width() != -1) {
1493     strm << formatName;
1494     return;
1495   }
1496 
1497   PINDEX TitleWidth = 20;
1498   for (i = 0; i < options.GetSize(); i++) {
1499     PINDEX width =options[i].GetName().GetLength();
1500     if (width > TitleWidth)
1501       TitleWidth = width;
1502   }
1503 
1504   strm << right << setw(TitleWidth) <<   "Format Name" << left << "       = " << formatName << '\n'
1505        << right << setw(TitleWidth) <<    "Media Type" << left << "       = " << mediaType << '\n'
1506        << right << setw(TitleWidth) <<  "Payload Type" << left << "       = " << rtpPayloadType << '\n'
1507        << right << setw(TitleWidth) << "Encoding Name" << left << "       = " << rtpEncodingName << '\n';
1508   for (i = 0; i < options.GetSize(); i++) {
1509     const OpalMediaOption & option = options[i];
1510     strm << right << setw(TitleWidth) << option.GetName() << " (R/" << (option.IsReadOnly() ? 'O' : 'W')
1511          << ") = " << left << setw(10) << option;
1512 
1513 #if OPAL_SIP
1514     if (!option.GetFMTPName().IsEmpty())
1515       strm << "  FMTP name: " << option.GetFMTPName() << " (" << option.GetFMTPDefault() << ')';
1516 #endif // OPAL_SIP
1517 
1518 #if OPAL_H323
1519     const OpalMediaOption::H245GenericInfo & genericInfo = option.GetH245Generic();
1520     if (genericInfo.mode != OpalMediaOption::H245GenericInfo::None) {
1521       strm << "  H.245 Ordinal: " << genericInfo.ordinal
1522            << ' ' << (genericInfo.mode == OpalMediaOption::H245GenericInfo::Collapsing ? "Collapsing" : "Non-Collapsing");
1523       if (!genericInfo.excludeTCS)
1524         strm << " TCS";
1525       if (!genericInfo.excludeOLC)
1526         strm << " OLC";
1527       if (!genericInfo.excludeReqMode)
1528         strm << " RM";
1529     }
1530 #endif // OPAL_H323
1531 
1532     // Show the type of the option: Boolean, Unsigned, String, etc.
1533     if (PIsDescendant(&option, OpalMediaOptionBoolean))
1534       strm << " Boolean";
1535     else if (PIsDescendant(&option, OpalMediaOptionUnsigned))
1536 #if OPAL_H323
1537       switch (genericInfo.integerType) {
1538         default :
1539         case OpalMediaOption::H245GenericInfo::UnsignedInt :
1540           strm << " UnsignedInt";
1541           break;
1542         case OpalMediaOption::H245GenericInfo::Unsigned32 :
1543           strm << " Unsigned32";
1544           break;
1545         case OpalMediaOption::H245GenericInfo::BooleanArray :
1546           strm << " BooleanArray";
1547           break;
1548       }
1549 #else
1550       strm << " UnsignedInt";
1551 #endif // OPAL_H323
1552     else if (PIsDescendant(&option, OpalMediaOptionOctets))
1553       strm << " OctetString";
1554     else if (PIsDescendant(&option, OpalMediaOptionString))
1555       strm << " String";
1556     else if (PIsDescendant(&option, OpalMediaOptionEnum))
1557       strm << " Enum";
1558     else
1559       strm << " Unknown";
1560 
1561     strm << '\n';
1562   }
1563   strm << endl;
1564 }
1565 
1566 ///////////////////////////////////////////////////////////////////////////////
1567 
RxFramesPerPacketOption()1568 const PString & OpalAudioFormat::RxFramesPerPacketOption() { static const PConstString s(PLUGINCODEC_OPTION_RX_FRAMES_PER_PACKET); return s; }
TxFramesPerPacketOption()1569 const PString & OpalAudioFormat::TxFramesPerPacketOption() { static const PConstString s(PLUGINCODEC_OPTION_TX_FRAMES_PER_PACKET); return s; }
MaxFramesPerPacketOption()1570 const PString & OpalAudioFormat::MaxFramesPerPacketOption(){ static const PConstString s("Max Frames Per Packet"); return s; }
ChannelsOption()1571 const PString & OpalAudioFormat::ChannelsOption()          { static const PConstString s("Channels"); return s; }
1572 
OpalAudioFormat(const char * fullName,RTP_DataFrame::PayloadTypes rtpPayloadType,const char * encodingName,PINDEX frameSize,unsigned frameTime,unsigned rxFrames,unsigned txFrames,unsigned maxFrames,unsigned clockRate,time_t timeStamp)1573 OpalAudioFormat::OpalAudioFormat(const char * fullName,
1574                                  RTP_DataFrame::PayloadTypes rtpPayloadType,
1575                                  const char * encodingName,
1576                                  PINDEX   frameSize,
1577                                  unsigned frameTime,
1578                                  unsigned rxFrames,
1579                                  unsigned txFrames,
1580                                  unsigned maxFrames,
1581                                  unsigned clockRate,
1582                                  time_t timeStamp)
1583 {
1584   Construct(new OpalAudioFormatInternal(fullName,
1585                                         rtpPayloadType,
1586                                         encodingName,
1587                                         frameSize,
1588                                         frameTime,
1589                                         rxFrames,
1590                                         txFrames,
1591                                         maxFrames,
1592                                         clockRate,
1593                                         timeStamp));
1594 }
1595 
1596 
OpalAudioFormatInternal(const char * fullName,RTP_DataFrame::PayloadTypes rtpPayloadType,const char * encodingName,PINDEX frameSize,unsigned frameTime,unsigned rxFrames,unsigned txFrames,unsigned maxFrames,unsigned clockRate,time_t timeStamp)1597 OpalAudioFormatInternal::OpalAudioFormatInternal(const char * fullName,
1598                                                  RTP_DataFrame::PayloadTypes rtpPayloadType,
1599                                                  const char * encodingName,
1600                                                  PINDEX   frameSize,
1601                                                  unsigned frameTime,
1602                                                  unsigned rxFrames,
1603                                                  unsigned txFrames,
1604                                                  unsigned maxFrames,
1605                                                  unsigned clockRate,
1606                                                  time_t timeStamp)
1607   : OpalMediaFormatInternal(fullName,
1608                             "audio",
1609                             rtpPayloadType,
1610                             encodingName,
1611                             true,
1612                             8*frameSize*clockRate/frameTime,  // bits per second = 8*frameSize * framesPerSecond
1613                             frameSize,
1614                             frameTime,
1615                             clockRate,
1616                             timeStamp)
1617 {
1618   if (rxFrames > 0)
1619     AddOption(new OpalMediaOptionUnsigned(OpalAudioFormat::RxFramesPerPacketOption(), false, OpalMediaOption::NoMerge,  rxFrames, 1, maxFrames));
1620   if (txFrames > 0)
1621     AddOption(new OpalMediaOptionUnsigned(OpalAudioFormat::TxFramesPerPacketOption(), false, OpalMediaOption::AlwaysMerge, txFrames, 1, maxFrames));
1622 
1623   AddOption(new OpalMediaOptionUnsigned(OpalAudioFormat::MaxFramesPerPacketOption(), true,  OpalMediaOption::NoMerge,  maxFrames));
1624   AddOption(new OpalMediaOptionUnsigned(OpalAudioFormat::ChannelsOption(),           false, OpalMediaOption::NoMerge,  m_channels, 1, 5));
1625 }
1626 
1627 
Clone() const1628 PObject * OpalAudioFormatInternal::Clone() const
1629 {
1630   PWaitAndSignal m(media_format_mutex);
1631   return new OpalAudioFormatInternal(*this);
1632 }
1633 
1634 
Merge(const OpalMediaFormatInternal & mediaFormat)1635 bool OpalAudioFormatInternal::Merge(const OpalMediaFormatInternal & mediaFormat)
1636 {
1637   PWaitAndSignal m1(media_format_mutex);
1638   PWaitAndSignal m2(mediaFormat.media_format_mutex);
1639 
1640   if (!OpalMediaFormatInternal::Merge(mediaFormat))
1641     return false;
1642 
1643   Clamp(*this, mediaFormat, OpalAudioFormat::TxFramesPerPacketOption(), PString::Empty(), OpalAudioFormat::RxFramesPerPacketOption());
1644   return true;
1645 }
1646 
1647 
1648 ///////////////////////////////////////////////////////////////////////////////
1649 
1650 #if OPAL_VIDEO
1651 
FrameWidthOption()1652 const PString & OpalVideoFormat::FrameWidthOption()               { static const PConstString s(PLUGINCODEC_OPTION_FRAME_WIDTH);               return s; }
FrameHeightOption()1653 const PString & OpalVideoFormat::FrameHeightOption()              { static const PConstString s(PLUGINCODEC_OPTION_FRAME_HEIGHT);              return s; }
MinRxFrameWidthOption()1654 const PString & OpalVideoFormat::MinRxFrameWidthOption()          { static const PConstString s(PLUGINCODEC_OPTION_MIN_RX_FRAME_WIDTH);        return s; }
MinRxFrameHeightOption()1655 const PString & OpalVideoFormat::MinRxFrameHeightOption()         { static const PConstString s(PLUGINCODEC_OPTION_MIN_RX_FRAME_HEIGHT);       return s; }
MaxRxFrameWidthOption()1656 const PString & OpalVideoFormat::MaxRxFrameWidthOption()          { static const PConstString s(PLUGINCODEC_OPTION_MAX_RX_FRAME_WIDTH);        return s; }
MaxRxFrameHeightOption()1657 const PString & OpalVideoFormat::MaxRxFrameHeightOption()         { static const PConstString s(PLUGINCODEC_OPTION_MAX_RX_FRAME_HEIGHT);       return s; }
TemporalSpatialTradeOffOption()1658 const PString & OpalVideoFormat::TemporalSpatialTradeOffOption()  { static const PConstString s(PLUGINCODEC_OPTION_TEMPORAL_SPATIAL_TRADE_OFF);return s; }
TxKeyFramePeriodOption()1659 const PString & OpalVideoFormat::TxKeyFramePeriodOption()         { static const PConstString s(PLUGINCODEC_OPTION_TX_KEY_FRAME_PERIOD);       return s; }
RateControlPeriodOption()1660 const PString & OpalVideoFormat::RateControlPeriodOption()        { static const PConstString s(PLUGINCODEC_OPTION_RATE_CONTROL_PERIOD);       return s; }
RateControllerOption()1661 const PString & OpalVideoFormat::RateControllerOption()           { static const PConstString s("Rate Controller");                            return s; }
ContentRoleOption()1662 const PString & OpalVideoFormat::ContentRoleOption()              { static const PConstString s("Content Role");                               return s; }
ContentRoleMaskOption()1663 const PString & OpalVideoFormat::ContentRoleMaskOption()          { static const PConstString s("Content Role Mask");                          return s; }
1664 
OpalVideoFormat(const char * fullName,RTP_DataFrame::PayloadTypes rtpPayloadType,const char * encodingName,unsigned maxFrameWidth,unsigned maxFrameHeight,unsigned maxFrameRate,unsigned maxBitRate,time_t timeStamp)1665 OpalVideoFormat::OpalVideoFormat(const char * fullName,
1666                                  RTP_DataFrame::PayloadTypes rtpPayloadType,
1667                                  const char * encodingName,
1668                                  unsigned maxFrameWidth,
1669                                  unsigned maxFrameHeight,
1670                                  unsigned maxFrameRate,
1671                                  unsigned maxBitRate,
1672                                  time_t timeStamp)
1673 {
1674   Construct(new OpalVideoFormatInternal(fullName,
1675                                         rtpPayloadType,
1676                                         encodingName,
1677                                         maxFrameWidth,
1678                                         maxFrameHeight,
1679                                         maxFrameRate,
1680                                         maxBitRate,
1681                                         timeStamp));
1682 }
1683 
1684 
OpalVideoFormatInternal(const char * fullName,RTP_DataFrame::PayloadTypes rtpPayloadType,const char * encodingName,unsigned maxFrameWidth,unsigned maxFrameHeight,unsigned maxFrameRate,unsigned maxBitRate,time_t timeStamp)1685 OpalVideoFormatInternal::OpalVideoFormatInternal(const char * fullName,
1686                                                  RTP_DataFrame::PayloadTypes rtpPayloadType,
1687                                                  const char * encodingName,
1688                                                  unsigned maxFrameWidth,
1689                                                  unsigned maxFrameHeight,
1690                                                  unsigned maxFrameRate,
1691                                                  unsigned maxBitRate,
1692                                                  time_t timeStamp)
1693   : OpalMediaFormatInternal(fullName,
1694                             "video",
1695                             rtpPayloadType,
1696                             encodingName,
1697                             PFalse,
1698                             maxBitRate,
1699                             0,
1700                             OpalMediaFormat::VideoClockRate/maxFrameRate,
1701                             OpalMediaFormat::VideoClockRate,
1702                             timeStamp)
1703 {
1704   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::FrameWidthOption(),               false, OpalMediaOption::AlwaysMerge, PVideoFrameInfo::CIFWidth,   16,  32767));
1705   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::FrameHeightOption(),              false, OpalMediaOption::AlwaysMerge, PVideoFrameInfo::CIFHeight,  16,  32767));
1706   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::MinRxFrameWidthOption(),          false, OpalMediaOption::MaxMerge,    PVideoFrameInfo::SQCIFWidth, 16,  32767));
1707   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::MinRxFrameHeightOption(),         false, OpalMediaOption::MaxMerge,    PVideoFrameInfo::SQCIFHeight,16,  32767));
1708   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::MaxRxFrameWidthOption(),          false, OpalMediaOption::MinMerge,    maxFrameWidth,               16,  32767));
1709   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::MaxRxFrameHeightOption(),         false, OpalMediaOption::MinMerge,    maxFrameHeight,              16,  32767));
1710   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::TargetBitRateOption(),            false, OpalMediaOption::AlwaysMerge, maxBitRate,                  1000      ));
1711   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::TxKeyFramePeriodOption(),         false, OpalMediaOption::AlwaysMerge, 125,                         0,    1000));
1712   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::RateControlPeriodOption(),        false, OpalMediaOption::AlwaysMerge, 1000,                        100, 60000));
1713   AddOption(new OpalMediaOptionUnsigned(OpalMediaFormat::MaxTxPacketSizeOption(),          true,  OpalMediaOption::AlwaysMerge, PluginCodec_RTP_MaxPayloadSize, 100    ));
1714   AddOption(new OpalMediaOptionString  (OpalVideoFormat::RateControllerOption(),           false                                                                       ));
1715 
1716 
1717   static const char * const RoleEnumerations[OpalVideoFormat::eNumRoles] = {
1718     "No Role",
1719     "Presentation",
1720     "Main",
1721     "Speaker",
1722     "Sign Language"
1723   };
1724   AddOption(new OpalMediaOptionEnum(OpalVideoFormat::ContentRoleOption(), false,
1725                                     RoleEnumerations, PARRAYSIZE(RoleEnumerations),
1726                                     OpalMediaOption::NoMerge));
1727 
1728   AddOption(new OpalMediaOptionUnsigned(OpalVideoFormat::ContentRoleMaskOption(),
1729                                         false, OpalMediaOption::IntersectionMerge,
1730                                         0, 0, OpalVideoFormat::ContentRoleMask));
1731 
1732   // For video the max bit rate and frame rate is adjustable by user
1733   FindOption(OpalVideoFormat::MaxBitRateOption())->SetReadOnly(false);
1734   FindOption(OpalVideoFormat::FrameTimeOption())->SetReadOnly(false);
1735   FindOption(OpalVideoFormat::FrameTimeOption())->SetMerge(OpalMediaOption::MaxMerge);
1736 }
1737 
1738 
Clone() const1739 PObject * OpalVideoFormatInternal::Clone() const
1740 {
1741   PWaitAndSignal m(media_format_mutex);
1742   return new OpalVideoFormatInternal(*this);
1743 }
1744 
1745 
Merge(const OpalMediaFormatInternal & mediaFormat)1746 bool OpalVideoFormatInternal::Merge(const OpalMediaFormatInternal & mediaFormat)
1747 {
1748   PWaitAndSignal m(media_format_mutex);
1749 
1750   if (!OpalMediaFormatInternal::Merge(mediaFormat))
1751     return false;
1752 
1753   Clamp(*this, mediaFormat, OpalVideoFormat::TargetBitRateOption(), PString::Empty(),                          OpalMediaFormat::MaxBitRateOption());
1754   Clamp(*this, mediaFormat, OpalVideoFormat::FrameWidthOption(),    OpalVideoFormat::MinRxFrameWidthOption(),  OpalVideoFormat::MaxRxFrameWidthOption());
1755   Clamp(*this, mediaFormat, OpalVideoFormat::FrameHeightOption(),   OpalVideoFormat::MinRxFrameHeightOption(), OpalVideoFormat::MaxRxFrameHeightOption());
1756 
1757   return true;
1758 }
1759 
1760 
AdjustVideoArgs(PVideoDevice::OpenArgs & args) const1761 void OpalMediaFormat::AdjustVideoArgs(PVideoDevice::OpenArgs & args) const
1762 {
1763   args.width = GetOptionInteger(OpalVideoFormat::FrameWidthOption(), PVideoFrameInfo::QCIFWidth);
1764   args.height = GetOptionInteger(OpalVideoFormat::FrameHeightOption(), PVideoFrameInfo::QCIFHeight);
1765   unsigned maxRate = GetClockRate()/GetFrameTime();
1766   if (args.rate > maxRate)
1767     args.rate = maxRate;
1768 }
1769 
1770 #endif // OPAL_VIDEO
1771 
1772 ///////////////////////////////////////////////////////////////////////////////
1773 
OpalMediaFormatList()1774 OpalMediaFormatList::OpalMediaFormatList()
1775 {
1776 }
1777 
1778 
OpalMediaFormatList(const OpalMediaFormat & format)1779 OpalMediaFormatList::OpalMediaFormatList(const OpalMediaFormat & format)
1780 {
1781   *this += format;
1782 }
1783 
1784 
operator +=(const PString & wildcard)1785 OpalMediaFormatList & OpalMediaFormatList::operator+=(const PString & wildcard)
1786 {
1787   MakeUnique();
1788 
1789   PWaitAndSignal mutex(GetMediaFormatsListMutex());
1790   OpalMediaFormatList & registeredFormats = GetMediaFormatsList();
1791 
1792   OpalMediaFormatList::const_iterator fmt;
1793   while ((fmt = registeredFormats.FindFormat(wildcard, fmt)) != registeredFormats.end()) {
1794     if (!HasFormat(*fmt))
1795       OpalMediaFormatBaseList::Append(fmt->Clone());
1796   }
1797 
1798   return *this;
1799 }
1800 
1801 
operator +=(const OpalMediaFormat & format)1802 OpalMediaFormatList & OpalMediaFormatList::operator+=(const OpalMediaFormat & format)
1803 {
1804   MakeUnique();
1805   if (format.IsValid() && !HasFormat(format))
1806     OpalMediaFormatBaseList::Append(format.Clone());
1807   return *this;
1808 }
1809 
1810 
operator +=(const OpalMediaFormatList & formats)1811 OpalMediaFormatList & OpalMediaFormatList::operator+=(const OpalMediaFormatList & formats)
1812 {
1813   MakeUnique();
1814   for (OpalMediaFormatList::const_iterator format = formats.begin(); format != formats.end(); ++format)
1815     *this += *format;
1816   return *this;
1817 }
1818 
1819 
operator -=(const OpalMediaFormat & format)1820 OpalMediaFormatList & OpalMediaFormatList::operator-=(const OpalMediaFormat & format)
1821 {
1822   MakeUnique();
1823   OpalMediaFormatList::const_iterator fmt = FindFormat(format);
1824   if (fmt != end())
1825     erase(fmt);
1826 
1827   return *this;
1828 }
1829 
1830 
operator -=(const OpalMediaFormatList & formats)1831 OpalMediaFormatList & OpalMediaFormatList::operator-=(const OpalMediaFormatList & formats)
1832 {
1833   MakeUnique();
1834   for (OpalMediaFormatList::const_iterator format = formats.begin(); format != formats.end(); ++format)
1835     *this -= *format;
1836   return *this;
1837 }
1838 
1839 
Remove(const PStringArray & maskList)1840 void OpalMediaFormatList::Remove(const PStringArray & maskList)
1841 {
1842   if (maskList.IsEmpty())
1843     return;
1844 
1845   PTRACE(4,"MediaFormat\tRemoving codecs " << setfill(',') << maskList);
1846 
1847   PINDEX i;
1848   PStringList notMasks;
1849   const_iterator fmt;
1850 
1851   for (i = 0; i < maskList.GetSize(); i++) {
1852     PString mask = maskList[i];
1853     if (mask[0] == '!')
1854       notMasks.AppendString(mask);
1855     else {
1856       while ((fmt = FindFormat(mask)) != end())
1857         erase(fmt);
1858     }
1859   }
1860 
1861   switch (notMasks.GetSize()) {
1862     case 0 :
1863       return;
1864 
1865     case 1 :
1866       while ((fmt = FindFormat(notMasks[0])) != end())
1867         erase(fmt);
1868       return;
1869   }
1870 
1871   OpalMediaFormatList formatsToKeep;
1872   for (i = 0; i < notMasks.GetSize(); i++) {
1873     PString keeper = notMasks[i].Mid(1);
1874     fmt = const_iterator();
1875     while ((fmt = FindFormat(keeper, fmt)) != end())
1876       formatsToKeep += *fmt;
1877   }
1878 
1879   *this = formatsToKeep;
1880 }
1881 
1882 
FindFormat(RTP_DataFrame::PayloadTypes pt,unsigned clockRate,const char * name,const char * protocol,const_iterator format) const1883 OpalMediaFormatList::const_iterator OpalMediaFormatList::FindFormat(RTP_DataFrame::PayloadTypes pt, unsigned clockRate, const char * name, const char * protocol, const_iterator format) const
1884 {
1885   if (format == const_iterator())
1886     format = begin();
1887   else
1888     ++format;
1889 
1890   // First look for a matching encoding name
1891   if (name != NULL && *name != '\0') {
1892     for (; format != end(); ++format) {
1893       // If encoding name matches exactly, then use it regardless of payload code.
1894       const char * otherName = format->GetEncodingName();
1895       if (otherName != NULL && strcasecmp(otherName, name) == 0 &&
1896           (clockRate == 0    || clockRate == format->GetClockRate()) && // if have clock rate, clock rate must match
1897           (protocol  == NULL || format->IsValidForProtocol(protocol))) // if protocol is specified, must be valid for the protocol
1898         return format;
1899     }
1900   }
1901 
1902   // Can't match by encoding name, try by known payload type.
1903   // Note we do two separate loops as it is possible (though discouraged) for
1904   // someone to override a standard payload type with another encoding name, so
1905   // have to search all formats by name before trying by number.
1906   if (pt < RTP_DataFrame::DynamicBase) {
1907     for (; format != end(); ++format) {
1908       if (format->GetPayloadType() == pt &&
1909           (clockRate == 0    || clockRate == format->GetClockRate()) && // if have clock rate, clock rate must match
1910           (protocol  == NULL || format->IsValidForProtocol(protocol))) // if protocol is specified, must be valid for the protocol
1911         return format;
1912     }
1913   }
1914 
1915   return end();
1916 }
1917 
1918 
WildcardMatch(const PCaselessString & str,const PStringArray & wildcards)1919 static bool WildcardMatch(const PCaselessString & str, const PStringArray & wildcards)
1920 {
1921   if (wildcards.GetSize() == 1)
1922     return str == wildcards[0];
1923 
1924   PINDEX i;
1925   PINDEX last = 0;
1926   for (i = 0; i < wildcards.GetSize(); i++) {
1927     PString wildcard = wildcards[i];
1928 
1929     PINDEX next;
1930     if (wildcard.IsEmpty())
1931       next = last;
1932     else {
1933       next = str.Find(wildcard, last);
1934       if (next == P_MAX_INDEX)
1935         return false;
1936     }
1937 
1938     // Check for having * at beginning of search string
1939     if (i == 0 && next != 0 && !wildcard)
1940       return false;
1941 
1942     last = next + wildcard.GetLength();
1943 
1944     // Check for having * at end of search string
1945     if (i == wildcards.GetSize()-1 && !wildcard && last != str.GetLength())
1946       return false;
1947   }
1948 
1949   return true;
1950 }
1951 
1952 
FindFormat(const PString & search,const_iterator iter) const1953 OpalMediaFormatList::const_iterator OpalMediaFormatList::FindFormat(const PString & search, const_iterator iter) const
1954 {
1955   if (search.IsEmpty())
1956     return end();
1957 
1958   if (iter == const_iterator())
1959     iter = begin();
1960   else
1961     ++iter;
1962 
1963   bool negative = search[0] == '!';
1964 
1965   PString adjustedSearch = search.Mid(negative ? 1 : 0);
1966   if (adjustedSearch.IsEmpty())
1967     return end();
1968 
1969   if (adjustedSearch[0] == '@') {
1970     OpalMediaType searchType = adjustedSearch.Mid(1);
1971     while (iter != end()) {
1972       if ((iter->GetMediaType() == searchType) != negative)
1973         return iter;
1974       ++iter;
1975     }
1976   }
1977   else {
1978     PStringArray wildcards = adjustedSearch.Tokenise('*', true);
1979     while (iter != end()) {
1980       if (WildcardMatch(iter->m_info->formatName, wildcards) != negative)
1981         return iter;
1982       ++iter;
1983     }
1984   }
1985 
1986   return end();
1987 }
1988 
1989 
Reorder(const PStringArray & order)1990 void OpalMediaFormatList::Reorder(const PStringArray & order)
1991 {
1992   DisallowDeleteObjects();
1993   PINDEX nextPos = 0;
1994   for (PINDEX i = 0; i < order.GetSize(); i++) {
1995     if (order[i][0] == '@') {
1996       OpalMediaType mediaType = order[i].Mid(1);
1997       PINDEX findPos = 0;
1998       while (findPos < GetSize()) {
1999         if ((*this)[findPos].GetMediaType() == mediaType) {
2000           if (findPos > nextPos)
2001             OpalMediaFormatBaseList::InsertAt(nextPos, RemoveAt(findPos));
2002           nextPos++;
2003         }
2004         findPos++;
2005       }
2006     }
2007     else {
2008       PStringArray wildcards = order[i].Tokenise('*', true);
2009 
2010       PINDEX findPos = 0;
2011       while (findPos < GetSize()) {
2012         if (WildcardMatch((*this)[findPos].m_info->formatName, wildcards)) {
2013           if (findPos > nextPos)
2014             OpalMediaFormatBaseList::InsertAt(nextPos, RemoveAt(findPos));
2015           nextPos++;
2016         }
2017         findPos++;
2018       }
2019     }
2020   }
2021   AllowDeleteObjects();
2022 }
2023 
HasType(const OpalMediaType & type,bool mustBeTransportable) const2024 bool OpalMediaFormatList::HasType(const OpalMediaType & type, bool mustBeTransportable) const
2025 {
2026   OpalMediaFormatList::const_iterator format;
2027   for (format = begin(); format != end(); ++format) {
2028     if (format->GetMediaType() == type && (!mustBeTransportable || format->IsTransportable()))
2029       return true;
2030   }
2031 
2032   return false;
2033 }
2034 
2035 
2036 // End of File ///////////////////////////////////////////////////////////////
2037