Tidied some trace logs to assure all have a category (bit before a tab character...
[opal/cbnco.git] / src / opal / patch.cxx
blob3e279c0bbb1a2ebc0f7ccdd882a08bd1da587622
1 /*
2 * patch.cxx
4 * Media stream patch thread.
6 * Open Phone Abstraction Library (OPAL)
7 * Formally known as the Open H323 project.
9 * Copyright (c) 2001 Equivalence Pty. Ltd.
11 * The contents of this file are subject to the Mozilla Public License
12 * Version 1.0 (the "License"); you may not use this file except in
13 * compliance with the License. You may obtain a copy of the License at
14 * http://www.mozilla.org/MPL/
16 * Software distributed under the License is distributed on an "AS IS"
17 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18 * the License for the specific language governing rights and limitations
19 * under the License.
21 * The Original Code is Open Phone Abstraction Library.
23 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
25 * Contributor(s): ______________________________________.
27 * $Log$
28 * Revision 2.45 2007/04/02 05:51:33 rjongbloed
29 * Tidied some trace logs to assure all have a category (bit before a tab character) set.
31 * Revision 2.44 2007/03/29 08:30:21 csoutheren
32 * Avoid problem with T.38 codecs
34 * Revision 2.43 2007/03/29 05:22:42 csoutheren
35 * Add extra logging
37 * Revision 2.42 2007/03/01 03:23:00 csoutheren
38 * Ignore packets with no payload emitted by jitter buffer when no input available
40 * Revision 2.41 2007/02/10 21:50:36 dsandras
41 * Fixed potential deadlock if ReadPacket takes time to return or does not
42 * return. Thanks to Hannes Friederich for the proposal and the SUN Team
43 * for the bug report (Ekiga #404904).
45 * Revision 2.40 2007/02/05 19:43:17 dsandras
46 * Added additional mutex to prevent temporary deadlock when nothing is
47 * received on the remote media stream during the establishment phase.
49 * Revision 2.39 2007/01/25 11:48:11 hfriederich
50 * OpalMediaPatch code refactorization.
51 * Split into OpalMediaPatch (using a thread) and OpalPassiveMediaPatch
52 * (not using a thread). Also adds the possibility for source streams
53 * to push frames down to the sink streams instead of having a patch
54 * thread around.
56 * Revision 2.38 2006/12/08 05:13:10 csoutheren
57 * Applied 1603783 - To allow media streams to handle more then one patch
58 * Thanks to jmatela
60 * Revision 2.37 2006/10/06 05:33:12 hfriederich
61 * Fix RFC2833 for SIP connections
63 * Revision 2.36 2006/07/24 14:03:40 csoutheren
64 * Merged in audio and video plugins from CVS branch PluginBranch
66 * Revision 2.35 2006/07/14 05:24:50 csoutheren
67 * Applied 1509232 - Fix for a bug in OpalMediaPatch::Close method
68 * Thanks to Borko Jandras
70 * Revision 2.34 2006/07/14 04:22:43 csoutheren
71 * Applied 1517397 - More Phobos stability fix
72 * Thanks to Dinis Rosario
74 * Revision 2.33 2006/07/04 00:48:14 csoutheren
75 * New version of patch 1509246
77 * Revision 2.32 2006/06/30 09:20:37 dsandras
78 * Fixed wrong assertion triggering.
80 * Revision 2.31 2006/06/30 07:36:37 csoutheren
81 * Applied 1495026 - Avoid deadlock if mediaPatchThread has never been started
82 * Thanks to mturconi
84 * Revision 2.30 2006/06/30 05:33:26 csoutheren
85 * Applied 1509251 - Locking rearrangement in OpalMediaPatch::Main
86 * Thanks to Borko Jandras
88 * Revision 2.29 2006/06/30 05:23:47 csoutheren
89 * Applied 1509246 - Fix sleeping in OpalMediaPatch::Main
90 * Thanks to Borko Jandras
92 * Revision 2.28 2006/06/30 01:33:43 csoutheren
93 * Add function to get patch sink media format
95 * Revision 2.27 2006/06/28 11:29:07 csoutheren
96 * Patch 1456858 - Add mutex to transaction dictionary and other stability patches
97 * Thanks to drosario
99 * Revision 2.26 2006/06/27 12:08:01 csoutheren
100 * Patch 1455568 - RFC2833 patch
101 * Thanks to Boris Pavacic
103 * Revision 2.25 2006/06/03 12:42:36 shorne
104 * Fix compile error on MSVC6
106 * Revision 2.24 2006/05/07 15:33:54 dsandras
107 * Reverted the last part of the patch.
109 * Revision 2.23 2006/05/07 14:03:04 dsandras
110 * Reverted patch 2.21 which could cause some deadlocks with H.323.
112 * Revision 2.22 2006/04/09 12:12:54 rjongbloed
113 * Changed the media format option merging to include the transcoder formats.
115 * Revision 2.21 2006/03/20 10:37:47 csoutheren
116 * Applied patch #1453753 - added locking on media stream manipulation
117 * Thanks to Dinis Rosario
119 * Revision 2.20 2006/02/02 07:02:58 csoutheren
120 * Added RTP payload map to transcoders and connections to allow remote SIP endpoints
121 * to change the payload type used for outgoing RTP.
123 * Revision 2.19 2005/12/30 14:33:12 dsandras
124 * Added support for Packet Loss Concealment frames for framed codecs supporting it similarly to what was done for OpenH323.
126 * Revision 2.18 2005/12/21 20:39:15 dsandras
127 * Prevent recursion when executing a command on a stream.
129 * Revision 2.17 2005/11/25 21:02:19 dsandras
130 * Remove the filters when closing the OpalMediaPatch.
132 * Revision 2.16 2005/10/20 20:28:18 dsandras
133 * Avoid the thread to keep the priority for a too long time.
135 * Revision 2.15 2005/09/06 12:44:49 rjongbloed
136 * Many fixes to finalise the video processing: merging remote media
138 * Revision 2.14 2005/09/04 06:23:39 rjongbloed
139 * Added OpalMediaCommand mechanism (via PNotifier) for media streams
140 * and media transcoders to send commands back to remote.
142 * Revision 2.13 2005/08/31 13:19:25 rjongbloed
143 * Added mechanism for controlling media (especially codecs) including
144 * changing the OpalMediaFormat option list (eg bit rate) and a completely
145 * new OpalMediaCommand abstraction for things like video fast update.
147 * Revision 2.12 2005/07/24 07:42:29 rjongbloed
148 * Fixed various video media stream issues.
150 * Revision 2.11 2004/08/16 09:53:48 rjongbloed
151 * Fixed possible deadlock in PTRACE output of media patch.
153 * Revision 2.10 2004/08/15 10:10:28 rjongbloed
154 * Fixed possible deadlock when closing media patch
156 * Revision 2.9 2004/08/14 07:56:43 rjongbloed
157 * Major revision to utilise the PSafeCollection classes for the connections and calls.
159 * Revision 2.8 2004/05/17 13:24:18 rjongbloed
160 * Added silence suppression.
162 * Revision 2.7 2004/04/25 02:53:29 rjongbloed
163 * Fixed GNU 3.4 warnings
165 * Revision 2.6 2004/02/15 04:34:08 rjongbloed
166 * Fixed correct setting of write data size on sick stream. Important for current
167 * output of silence frames and adjustment of sound card buffers.
168 * Fixed correct propagation of timestamp values from source to sink media
169 * stream and back from sink to source stream.
171 * Revision 2.5 2004/01/18 15:35:21 rjongbloed
172 * More work on video support
174 * Revision 2.4 2003/03/17 10:27:00 robertj
175 * Added video support.
177 * Revision 2.3 2002/03/07 02:25:52 craigs
178 * Patch threads now take notice of failed writes by removing the offending sink from the list
180 * Revision 2.2 2002/01/22 05:13:15 robertj
181 * Added filter functions to media patch.
183 * Revision 2.1 2002/01/14 02:19:03 robertj
184 * Added ability to turn jitter buffer off in media stream to allow for patches
185 * that do not require it.
187 * Revision 2.0 2001/07/27 15:48:25 robertj
188 * Conversion of OpenH323 to Open Phone Abstraction Library (OPAL)
192 #include <ptlib.h>
194 #ifdef __GNUC__
195 #pragma implementation "patch.h"
196 #endif
198 #include <opal/patch.h>
200 #include <opal/mediastrm.h>
201 #include <opal/transcoders.h>
204 #define new PNEW
207 /////////////////////////////////////////////////////////////////////////////
209 OpalMediaPatch::OpalMediaPatch(OpalMediaStream & src)
210 : source(src)
212 src.SetPatch(this);
213 patchThread = NULL;
217 OpalMediaPatch::~OpalMediaPatch()
219 PWaitAndSignal m(patchThreadMutex);
220 inUse.Wait();
221 delete patchThread;
222 PTRACE(3, "Patch\tMedia patch thread " << *this << " destroyed.");
226 void OpalMediaPatch::PrintOn(ostream & strm) const
228 strm << "Patch " << source;
230 // Have timed mutex so avoid deadlocks in PTRACE(), it is nice to
231 // get all the sinks in the PrintOn, we don't HAVE to have it.
232 if (inUse.Wait(20)) {
234 if (sinks.GetSize() > 0) {
235 strm << " -> ";
236 if (sinks.GetSize() == 1)
237 strm << *sinks[0].stream;
238 else {
239 for (PINDEX i = 0; i < sinks.GetSize(); i++) {
240 if (i > 0)
241 strm << ", ";
242 strm << "sink[" << i << "]=" << *sinks[i].stream;
247 inUse.Signal();
251 void OpalMediaPatch::Start()
253 PWaitAndSignal m(patchThreadMutex);
255 if(patchThread != NULL)
256 return;
258 patchThread = new Thread(*this);
259 patchThread->Resume();
260 PThread::Yield();
261 PTRACE(4, "Media\tStarting thread " << patchThread->GetThreadName());
265 void OpalMediaPatch::Close()
267 PTRACE(3, "Patch\tClosing media patch " << *this);
269 inUse.Wait();
270 filters.RemoveAll();
271 source.Close();
273 while (sinks.GetSize() > 0) {
274 OpalMediaStream * stream = sinks[0].stream;
275 stream->GetDeleteMutex().Wait();
276 inUse.Signal();
277 stream->RemovePatch(this);
278 inUse.Wait();
279 stream->GetDeleteMutex().Signal();
280 RemoveSink(stream);
283 PTRACE(3, "Patch\tWaiting for media patch thread to stop " << *this);
284 if (patchThread != NULL && !patchThread->IsSuspended()) {
285 inUse.Signal();
286 PAssert(patchThread->WaitForTermination(10000), "Media patch thread not terminated.");
287 return;
290 inUse.Signal();
294 BOOL OpalMediaPatch::AddSink(OpalMediaStream * stream, const RTP_DataFrame::PayloadMapType & rtpMap)
296 if (PAssertNULL(stream) == NULL)
297 return FALSE;
299 PAssert(stream->IsSink(), "Attempt to set source stream as sink!");
301 PWaitAndSignal mutex(inUse);
303 Sink * sink = new Sink(*this, stream);
304 sinks.Append(sink);
306 stream->SetPatch(this);
308 // Find the media formats than can be used to get from source to sink
309 OpalMediaFormat sourceFormat = source.GetMediaFormat();
310 OpalMediaFormat destinationFormat = stream->GetMediaFormat();
312 #if PTRACING
313 ostream & traceStream = PTrace::Begin(4, __FILE__, __LINE__);
314 traceStream << "Patch\tAdded sink\n from " << sourceFormat << '\n';
315 for (PINDEX i = 0; i < sourceFormat.GetOptionCount(); i++) {
316 const OpalMediaOption & option = sourceFormat.GetOption(i);
317 traceStream << " " << option.GetName() << " = " << option.AsString() << '\n';
319 traceStream << " to " << destinationFormat << '\n';
320 for (PINDEX i = 0; i < destinationFormat.GetOptionCount(); i++) {
321 const OpalMediaOption & option = destinationFormat.GetOption(i);
322 traceStream << " " << option.GetName() << " = " << option.AsString() << '\n';
324 traceStream << PTrace::End;
325 #endif
327 if ((sourceFormat == destinationFormat) && ((sourceFormat.GetDefaultSessionID() == OpalMediaFormat::DefaultDataSessionID) || (source.GetDataSize() <= stream->GetDataSize()))) {
328 PTRACE(3, "Patch\tAdded direct media stream sink " << *stream);
329 return TRUE;
332 PString id = stream->GetID();
333 sink->primaryCodec = OpalTranscoder::Create(sourceFormat, destinationFormat, (const BYTE *)id, id.GetLength());
334 if (sink->primaryCodec != NULL) {
335 PTRACE(4, "Patch\tCreated primary codec " << sourceFormat << "/" << destinationFormat << " with ID " << id);
336 sink->primaryCodec->SetRTPPayloadMap(rtpMap);
337 sink->primaryCodec->SetMaxOutputSize(stream->GetDataSize());
339 if (!stream->SetDataSize(sink->primaryCodec->GetOptimalDataFrameSize(FALSE))) {
340 PTRACE(2, "Patch\tSink stream " << *stream << " cannot support data size "
341 << sink->primaryCodec->GetOptimalDataFrameSize(FALSE));
342 return FALSE;
345 PTRACE(3, "Patch\tAdded media stream sink " << *stream
346 << " using transcoder " << *sink->primaryCodec);
348 else {
349 OpalMediaFormat intermediateFormat;
350 if (!OpalTranscoder::FindIntermediateFormat(sourceFormat, destinationFormat,
351 intermediateFormat)) {
352 PTRACE(2, "Patch\tCould find compatible media format for " << *stream);
353 return FALSE;
356 sink->primaryCodec = OpalTranscoder::Create(sourceFormat, intermediateFormat, (const BYTE *)id, id.GetLength());
357 sink->secondaryCodec = OpalTranscoder::Create(intermediateFormat, destinationFormat, (const BYTE *)id, id.GetLength());
359 PTRACE(4, "Patch\tcreated two stage codec " << sourceFormat << "/" << intermediateFormat << "/" << destinationFormat << " with ID " << id);
361 sink->secondaryCodec->SetMaxOutputSize(sink->stream->GetDataSize());
363 if (!stream->SetDataSize(sink->secondaryCodec->GetOptimalDataFrameSize(FALSE))) {
364 PTRACE(2, "Patch\tSink stream " << *stream << " cannot support data size "
365 << sink->secondaryCodec->GetOptimalDataFrameSize(FALSE));
366 return FALSE;
369 PTRACE(3, "Patch\tAdded media stream sink " << *stream
370 << " using transcoders " << *sink->primaryCodec
371 << " and " << *sink->secondaryCodec);
374 source.SetDataSize(sink->primaryCodec->GetOptimalDataFrameSize(TRUE));
375 return TRUE;
379 void OpalMediaPatch::RemoveSink(OpalMediaStream * stream)
381 if (PAssertNULL(stream) == NULL)
382 return;
384 PTRACE(3, "Patch\tRemoving media stream sink " << *stream);
386 PWaitAndSignal mutex(inUse);
388 for (PINDEX i = 0; i < sinks.GetSize(); i++) {
389 if (sinks[i].stream == stream) {
390 sinks.RemoveAt(i);
391 return;
396 OpalMediaFormat OpalMediaPatch::GetSinkFormat(PINDEX i) const
398 OpalMediaFormat fmt;
400 PWaitAndSignal mutex(inUse);
402 if (i >= sinks.GetSize())
403 return fmt;
405 Sink & sink = sinks[i];
406 if (sink.secondaryCodec != NULL)
407 return sink.secondaryCodec->GetOutputFormat();
409 if (sink.primaryCodec != NULL)
410 return sink.primaryCodec->GetOutputFormat();
412 return fmt;
415 OpalMediaPatch::Sink::Sink(OpalMediaPatch & p, OpalMediaStream * s)
416 : patch(p)
418 stream = s;
419 primaryCodec = NULL;
420 secondaryCodec = NULL;
421 intermediateFrames.Append(new RTP_DataFrame);
422 finalFrames.Append(new RTP_DataFrame);
423 writeSuccessful = true;
427 OpalMediaPatch::Sink::~Sink()
429 delete primaryCodec;
430 delete secondaryCodec;
434 void OpalMediaPatch::AddFilter(const PNotifier & filter, const OpalMediaFormat & stage)
436 PWaitAndSignal mutex(inUse);
438 // ensures that a filter is added only once
439 for (PINDEX i = 0; i < filters.GetSize(); i++) {
440 if (filters[i].notifier == filter && filters[i].stage == stage) {
441 return;
444 filters.Append(new Filter(filter, stage));
448 BOOL OpalMediaPatch::RemoveFilter(const PNotifier & filter, const OpalMediaFormat & stage)
450 PWaitAndSignal mutex(inUse);
452 for (PINDEX i = 0; i < filters.GetSize(); i++) {
453 if (filters[i].notifier == filter && filters[i].stage == stage) {
454 filters.RemoveAt(i);
455 return TRUE;
459 return FALSE;
463 void OpalMediaPatch::FilterFrame(RTP_DataFrame & frame,
464 const OpalMediaFormat & mediaFormat)
466 PWaitAndSignal mutex(inUse);
467 for (PINDEX f = 0; f < filters.GetSize(); f++) {
468 Filter & filter = filters[f];
469 if (filter.stage.IsEmpty() || filter.stage == mediaFormat)
470 filter.notifier(frame, (INT)this);
475 BOOL OpalMediaPatch::UpdateMediaFormat(const OpalMediaFormat & mediaFormat, BOOL fromSink)
477 PWaitAndSignal mutex(inUse);
479 if (fromSink)
480 return source.UpdateMediaFormat(mediaFormat);
482 BOOL atLeastOne = FALSE;
483 for (PINDEX i = 0; i < sinks.GetSize(); i++)
484 atLeastOne = sinks[i].UpdateMediaFormat(mediaFormat) || atLeastOne;
486 return atLeastOne;
490 BOOL OpalMediaPatch::ExecuteCommand(const OpalMediaCommand & command, BOOL fromSink)
492 PWaitAndSignal mutex(inUse);
494 if (fromSink)
495 return source.ExecuteCommand(command);
497 BOOL atLeastOne = FALSE;
498 for (PINDEX i = 0; i < sinks.GetSize(); i++)
499 atLeastOne = sinks[i].ExecuteCommand(command) || atLeastOne;
501 return atLeastOne;
505 void OpalMediaPatch::SetCommandNotifier(const PNotifier & notifier, BOOL fromSink)
507 PWaitAndSignal mutex(inUse);
509 if (fromSink)
510 source.SetCommandNotifier(notifier);
511 else {
512 for (PINDEX i = 0; i < sinks.GetSize(); i++)
513 sinks[i].SetCommandNotifier(notifier);
517 void OpalMediaPatch::Main()
519 PTRACE(3, "Patch\tThread started for " << *this);
520 PINDEX i;
522 inUse.Wait();
523 source.OnPatchStart();
524 BOOL isSynchronous = source.IsSynchronous();
525 if (!source.IsSynchronous()) {
526 for (i = 0; i < sinks.GetSize(); i++) {
527 if (sinks[i].stream->IsSynchronous()) {
528 source.EnableJitterBuffer();
529 isSynchronous = TRUE;
530 break;
535 inUse.Signal();
536 RTP_DataFrame sourceFrame(source.GetDataSize());
537 RTP_DataFrame emptyFrame(source.GetDataSize());
539 while (source.IsOpen()) {
540 if (!source.ReadPacket(sourceFrame))
541 break;
543 inUse.Wait();
545 if(!source.IsOpen() || sinks.GetSize() == 0) {
546 inUse.Signal();
547 break;
550 PINDEX len = sinks.GetSize();
552 if (sourceFrame.GetPayloadSize() > 0)
553 DispatchFrame(sourceFrame);
555 inUse.Signal();
557 if (!isSynchronous || !sourceFrame.GetPayloadSize())
558 PThread::Sleep(5); // Don't starve the CPU
559 #if !defined(WIN32)
560 else
561 PThread::Sleep(5); // Permit to another thread to take the mutex
562 #endif
564 if (len == 0)
565 break;
567 // make a new, clean frame, so that silence frame won't confuse RFC2833 handler
568 sourceFrame = emptyFrame;
571 PTRACE(3, "Patch\tThread ended for " << *this);
575 void OpalMediaPatch::DispatchFrame(RTP_DataFrame & frame)
577 FilterFrame(frame, source.GetMediaFormat());
579 PINDEX len = sinks.GetSize();
580 for (PINDEX i = 0; i < len; i++)
581 sinks[i].WriteFrame(frame);
585 bool OpalMediaPatch::Sink::UpdateMediaFormat(const OpalMediaFormat & mediaFormat)
587 if (secondaryCodec != NULL)
588 return secondaryCodec->UpdateOutputMediaFormat(mediaFormat);
590 if (primaryCodec != NULL)
591 return primaryCodec->UpdateOutputMediaFormat(mediaFormat);
593 return stream->UpdateMediaFormat(mediaFormat);
597 bool OpalMediaPatch::Sink::ExecuteCommand(const OpalMediaCommand & command)
599 BOOL atLeastOne = FALSE;
601 if (secondaryCodec != NULL)
602 atLeastOne = secondaryCodec->ExecuteCommand(command) || atLeastOne;
604 if (primaryCodec != NULL)
605 atLeastOne = primaryCodec->ExecuteCommand(command) || atLeastOne;
607 return atLeastOne;
611 void OpalMediaPatch::Sink::SetCommandNotifier(const PNotifier & notifier)
613 if (secondaryCodec != NULL)
614 secondaryCodec->SetCommandNotifier(notifier);
616 if (primaryCodec != NULL)
617 primaryCodec->SetCommandNotifier(notifier);
621 bool OpalMediaPatch::Sink::WriteFrame(RTP_DataFrame & sourceFrame)
623 if (!writeSuccessful)
624 return false;
626 if (primaryCodec == NULL)
627 return writeSuccessful = stream->WritePacket(sourceFrame);
629 if (!primaryCodec->ConvertFrames(sourceFrame, intermediateFrames)) {
630 PTRACE(1, "Patch\tMedia conversion (primary) failed");
631 return false;
634 if (sourceFrame.GetPayloadSize() == 0)
635 return writeSuccessful = stream->WritePacket(sourceFrame);
637 for (PINDEX i = 0; i < intermediateFrames.GetSize(); i++) {
638 RTP_DataFrame & intermediateFrame = intermediateFrames[i];
639 patch.FilterFrame(intermediateFrame, primaryCodec->GetOutputFormat());
640 if (secondaryCodec == NULL) {
641 if (!stream->WritePacket(intermediateFrame))
642 return writeSuccessful = false;
643 sourceFrame.SetTimestamp(intermediateFrame.GetTimestamp());
645 else {
646 if (!secondaryCodec->ConvertFrames(intermediateFrame, finalFrames)) {
647 PTRACE(1, "Patch\tMedia conversion (secondary) failed");
648 return false;
651 for (PINDEX f = 0; f < finalFrames.GetSize(); f++) {
652 RTP_DataFrame & finalFrame = finalFrames[f];
653 patch.FilterFrame(finalFrame, secondaryCodec->GetOutputFormat());
654 if (!stream->WritePacket(finalFrame))
655 return writeSuccessful = false;
656 sourceFrame.SetTimestamp(finalFrame.GetTimestamp());
661 return true;
664 OpalMediaPatch::Thread::Thread(OpalMediaPatch & p)
665 : PThread(65536, //16*4kpage size
666 NoAutoDeleteThread,
667 HighestPriority,
668 "Media Patch:%x"),
669 patch(p)
674 /////////////////////////////////////////////////////////////////////////////
677 OpalPassiveMediaPatch::OpalPassiveMediaPatch(OpalMediaStream & source)
678 : OpalMediaPatch(source)
683 void OpalPassiveMediaPatch::Start()
685 source.OnPatchStart();
689 BOOL OpalPassiveMediaPatch::PushFrame(RTP_DataFrame & frame)
691 DispatchFrame(frame);
692 return TRUE;
696 /////////////////////////////////////////////////////////////////////////////