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
21 * The Original Code is Open Phone Abstraction Library.
23 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
25 * Contributor(s): ______________________________________.
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
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
56 * Revision 2.38 2006/12/08 05:13:10 csoutheren
57 * Applied 1603783 - To allow media streams to handle more then one patch
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
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
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)
195 #pragma implementation "patch.h"
198 #include <opal/patch.h>
200 #include <opal/mediastrm.h>
201 #include <opal/transcoders.h>
207 /////////////////////////////////////////////////////////////////////////////
209 OpalMediaPatch::OpalMediaPatch(OpalMediaStream
& src
)
217 OpalMediaPatch::~OpalMediaPatch()
219 PWaitAndSignal
m(patchThreadMutex
);
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) {
236 if (sinks
.GetSize() == 1)
237 strm
<< *sinks
[0].stream
;
239 for (PINDEX i
= 0; i
< sinks
.GetSize(); i
++) {
242 strm
<< "sink[" << i
<< "]=" << *sinks
[i
].stream
;
251 void OpalMediaPatch::Start()
253 PWaitAndSignal
m(patchThreadMutex
);
255 if(patchThread
!= NULL
)
258 patchThread
= new Thread(*this);
259 patchThread
->Resume();
261 PTRACE(4, "Media\tStarting thread " << patchThread
->GetThreadName());
265 void OpalMediaPatch::Close()
267 PTRACE(3, "Patch\tClosing media patch " << *this);
273 while (sinks
.GetSize() > 0) {
274 OpalMediaStream
* stream
= sinks
[0].stream
;
275 stream
->GetDeleteMutex().Wait();
277 stream
->RemovePatch(this);
279 stream
->GetDeleteMutex().Signal();
283 PTRACE(3, "Patch\tWaiting for media patch thread to stop " << *this);
284 if (patchThread
!= NULL
&& !patchThread
->IsSuspended()) {
286 PAssert(patchThread
->WaitForTermination(10000), "Media patch thread not terminated.");
294 BOOL
OpalMediaPatch::AddSink(OpalMediaStream
* stream
, const RTP_DataFrame::PayloadMapType
& rtpMap
)
296 if (PAssertNULL(stream
) == NULL
)
299 PAssert(stream
->IsSink(), "Attempt to set source stream as sink!");
301 PWaitAndSignal
mutex(inUse
);
303 Sink
* sink
= new Sink(*this, stream
);
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();
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
;
327 if ((sourceFormat
== destinationFormat
) && ((sourceFormat
.GetDefaultSessionID() == OpalMediaFormat::DefaultDataSessionID
) || (source
.GetDataSize() <= stream
->GetDataSize()))) {
328 PTRACE(3, "Patch\tAdded direct media stream sink " << *stream
);
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
));
345 PTRACE(3, "Patch\tAdded media stream sink " << *stream
346 << " using transcoder " << *sink
->primaryCodec
);
349 OpalMediaFormat intermediateFormat
;
350 if (!OpalTranscoder::FindIntermediateFormat(sourceFormat
, destinationFormat
,
351 intermediateFormat
)) {
352 PTRACE(2, "Patch\tCould find compatible media format for " << *stream
);
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
));
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
));
379 void OpalMediaPatch::RemoveSink(OpalMediaStream
* stream
)
381 if (PAssertNULL(stream
) == NULL
)
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
) {
396 OpalMediaFormat
OpalMediaPatch::GetSinkFormat(PINDEX i
) const
400 PWaitAndSignal
mutex(inUse
);
402 if (i
>= sinks
.GetSize())
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();
415 OpalMediaPatch::Sink::Sink(OpalMediaPatch
& p
, OpalMediaStream
* s
)
420 secondaryCodec
= NULL
;
421 intermediateFrames
.Append(new RTP_DataFrame
);
422 finalFrames
.Append(new RTP_DataFrame
);
423 writeSuccessful
= true;
427 OpalMediaPatch::Sink::~Sink()
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
) {
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
) {
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
);
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
;
490 BOOL
OpalMediaPatch::ExecuteCommand(const OpalMediaCommand
& command
, BOOL fromSink
)
492 PWaitAndSignal
mutex(inUse
);
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
;
505 void OpalMediaPatch::SetCommandNotifier(const PNotifier
& notifier
, BOOL fromSink
)
507 PWaitAndSignal
mutex(inUse
);
510 source
.SetCommandNotifier(notifier
);
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);
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
;
536 RTP_DataFrame
sourceFrame(source
.GetDataSize());
537 RTP_DataFrame
emptyFrame(source
.GetDataSize());
539 while (source
.IsOpen()) {
540 if (!source
.ReadPacket(sourceFrame
))
545 if(!source
.IsOpen() || sinks
.GetSize() == 0) {
550 PINDEX len
= sinks
.GetSize();
552 if (sourceFrame
.GetPayloadSize() > 0)
553 DispatchFrame(sourceFrame
);
557 if (!isSynchronous
|| !sourceFrame
.GetPayloadSize())
558 PThread::Sleep(5); // Don't starve the CPU
561 PThread::Sleep(5); // Permit to another thread to take the mutex
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
;
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
)
626 if (primaryCodec
== NULL
)
627 return writeSuccessful
= stream
->WritePacket(sourceFrame
);
629 if (!primaryCodec
->ConvertFrames(sourceFrame
, intermediateFrames
)) {
630 PTRACE(1, "Patch\tMedia conversion (primary) failed");
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());
646 if (!secondaryCodec
->ConvertFrames(intermediateFrame
, finalFrames
)) {
647 PTRACE(1, "Patch\tMedia conversion (secondary) failed");
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());
664 OpalMediaPatch::Thread::Thread(OpalMediaPatch
& p
)
665 : PThread(65536, //16*4kpage size
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
);
696 /////////////////////////////////////////////////////////////////////////////