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