aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/help-pdp_ieee1394.pd47
-rw-r--r--doc/help-pdp_mp4live~.pd129
-rw-r--r--doc/help-pdp_mp4player~.pd17
-rw-r--r--doc/help-pdp_spotlight.pd76
-rw-r--r--doc/rs_pdp_mp4player~.pd57
-rw-r--r--include/pdp_mp4audiosource.h76
-rw-r--r--include/pdp_mp4audiosync.h115
-rw-r--r--include/pdp_mp4config.h381
-rw-r--r--include/pdp_mp4configset.h532
-rw-r--r--include/pdp_mp4playermedia.h232
-rw-r--r--include/pdp_mp4playersession.h249
-rw-r--r--include/pdp_mp4player~.h118
-rw-r--r--include/pdp_mp4rtpbytestream.h213
-rw-r--r--include/pdp_mp4videosource.h90
-rw-r--r--include/pdp_mp4videosync.h90
-rw-r--r--modules/pdp_ieee1394.c483
-rw-r--r--modules/pdp_mp4audiosource.cpp204
-rw-r--r--modules/pdp_mp4audiosync.cpp505
-rw-r--r--modules/pdp_mp4config.cpp136
-rw-r--r--modules/pdp_mp4live~.cpp638
-rw-r--r--modules/pdp_mp4playermedia.cpp2029
-rw-r--r--modules/pdp_mp4playersession.cpp1045
-rw-r--r--modules/pdp_mp4player~.cpp384
-rw-r--r--modules/pdp_mp4videosource.cpp185
-rw-r--r--modules/pdp_mp4videosync.cpp248
-rw-r--r--modules/pdp_spotlight.c320
-rw-r--r--patches/pdp_cabaret.pd119
27 files changed, 8718 insertions, 0 deletions
diff --git a/doc/help-pdp_ieee1394.pd b/doc/help-pdp_ieee1394.pd
new file mode 100644
index 0000000..3b90eea
--- /dev/null
+++ b/doc/help-pdp_ieee1394.pd
@@ -0,0 +1,47 @@
+#N canvas 237 22 740 692 10;
+#X obj 177 152 metro 70;
+#X obj 222 118 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 179 119 stop;
+#X obj 414 352 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 414 404 pdp_control;
+#X msg 414 377 thread \$1;
+#X floatatom 414 465 5 0 0 0 - - -;
+#X obj 414 436 route pdp_drop;
+#X text 33 439 pdp_ieee1394 : fire wire capture object for OSX;
+#X text 31 455 inspired by pix_video from Gem;
+#X text 31 469 written by Yves Degoyon (ydegoyon@free.fr);
+#X msg 296 154 open;
+#X text 339 155 Start Capture;
+#X msg 300 185 close;
+#X text 344 187 Stop Capture;
+#X text 179 97 This autostarts capture;
+#X msg 321 218 reset;
+#X text 366 220 Reset Capture;
+#X msg 288 267 quality \$1;
+#X obj 422 264 vradio 15 1 0 4 empty empty empty 0 -6 0 8 -262144 -1
+-1 0;
+#X text 446 262 Normal Quality;
+#X text 446 280 High Quality;
+#X text 446 295 Fast Quality;
+#X text 447 311 All data;
+#X text 171 329 Constructor : pdp_ieee1394 <width> <height>;
+#X text 363 245 Quality (set before starting capture !);
+#X obj 143 396 pdp_glx;
+#X obj 168 305 pdp_ieee1394 640 480;
+#X obj 166 358 pdp_scale 320 240;
+#X connect 0 0 27 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 5 0;
+#X connect 4 0 7 0;
+#X connect 5 0 4 0;
+#X connect 7 0 6 0;
+#X connect 11 0 27 0;
+#X connect 13 0 27 0;
+#X connect 16 0 27 0;
+#X connect 18 0 27 0;
+#X connect 19 0 18 0;
+#X connect 27 0 28 0;
+#X connect 28 0 26 0;
diff --git a/doc/help-pdp_mp4live~.pd b/doc/help-pdp_mp4live~.pd
new file mode 100644
index 0000000..d87d9e3
--- /dev/null
+++ b/doc/help-pdp_mp4live~.pd
@@ -0,0 +1,129 @@
+#N canvas 84 12 807 665 10;
+#X obj 268 64 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 93 136 loop \$1;
+#X obj 94 114 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1 1
+;
+#X msg 123 92 open \$1;
+#X obj 122 68 openpanel;
+#X obj 107 51 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 316 99 5 0 0 0 - - -;
+#X msg 225 65 stop;
+#X obj 323 68 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 257 134 metro 70;
+#X obj 365 156 pdp_v4l;
+#X obj 374 125 metro 70;
+#X obj 419 91 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 376 92 stop;
+#X obj 207 160 pdp_yqt;
+#X obj 606 530 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 606 582 pdp_control;
+#X msg 606 555 thread \$1;
+#X floatatom 606 643 5 0 0 0 - - -;
+#X obj 606 614 route pdp_drop;
+#X floatatom 220 496 5 0 0 0 - - -;
+#X text 268 497 Streaming status;
+#X floatatom 256 519 5 0 0 0 - - -;
+#X text 299 519 Number of video frames emitted;
+#X floatatom 294 539 5 0 0 0 - - -;
+#X obj 55 315 pdp_xv;
+#X obj 119 254 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 43 244 dac~;
+#X obj 40 211 spigot~;
+#X obj 119 254 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 87 183 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X msg 109 316 close;
+#X text 32 162 Local echoes;
+#X text 81 579 pdp_mp4live~ : mpeg4ip streaming emitter;
+#X text 0 390 Start the streaming;
+#X text 1 409 Stop the streaming;
+#X msg 142 411 disconnect;
+#X msg 142 390 connect;
+#X text 368 189 Step 1 : set streaming parameters;
+#X msg 363 229 videoport 47168;
+#X msg 364 250 audioport 47170;
+#X text 516 228 Set video port ( default : 7070 );
+#X text 515 250 Set audio port ( default : 8000 );
+#X text 516 209 Set ip address ( default : 127.0.0.1 );
+#X msg 365 269 ttl 1;
+#X text 515 269 Set ttl ( default : 15 );
+#X text 17 371 Step 3 : on air !!;
+#X text 515 291 Set video width ( default : 320 );
+#X msg 365 310 vheight 240;
+#X text 514 310 Set video height ( default : 240 );
+#X msg 365 330 framerate 15;
+#X text 515 331 Set video framerate ( default : 25 );
+#X text 516 352 Set video bitrate ( default : 128 );
+#X msg 365 351 vbitrate 256;
+#X msg 365 373 samplerate 22050;
+#X text 516 374 Set audio samplerate ( default : 44100 );
+#X text 516 395 Set audio bitrate ( default : 128 );
+#X msg 365 394 abitrate 64;
+#X text 368 420 Step 2 : save sdp file and upload it to your server
+;
+#X text 338 540 Frame Rate;
+#X obj 220 470 pdp_mp4live~;
+#X msg 366 436 sdp /usr/local/movies/pdstream.sdp;
+#X text 81 604 The rest is written by Yves Degoyon (ydegoyon@free.fr)
+;
+#X text 80 592 This object uses some code from mpeg4ip;
+#X msg 362 208 ipaddr 213.56.149.35;
+#X obj 207 200 pdp_scale 320 240;
+#X obj 273 231 adc~;
+#X msg 366 289 vwidth 320;
+#X obj 54 282 pdp_spigot;
+#X connect 0 0 9 0;
+#X connect 1 0 14 0;
+#X connect 2 0 1 0;
+#X connect 3 0 14 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 14 0;
+#X connect 10 0 60 0;
+#X connect 10 0 68 0;
+#X connect 11 0 10 0;
+#X connect 12 0 11 0;
+#X connect 13 0 11 0;
+#X connect 14 0 65 0;
+#X connect 14 0 68 0;
+#X connect 14 4 28 0;
+#X connect 14 5 28 0;
+#X connect 15 0 17 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 19 0 18 0;
+#X connect 26 0 68 1;
+#X connect 28 1 27 1;
+#X connect 28 1 27 0;
+#X connect 30 0 28 1;
+#X connect 31 0 25 0;
+#X connect 36 0 60 0;
+#X connect 37 0 60 0;
+#X connect 39 0 60 0;
+#X connect 40 0 60 0;
+#X connect 44 0 60 0;
+#X connect 48 0 60 0;
+#X connect 50 0 60 0;
+#X connect 53 0 60 0;
+#X connect 54 0 60 0;
+#X connect 57 0 60 0;
+#X connect 60 0 20 0;
+#X connect 60 1 22 0;
+#X connect 60 2 24 0;
+#X connect 61 0 60 0;
+#X connect 64 0 60 0;
+#X connect 65 0 60 0;
+#X connect 66 0 60 0;
+#X connect 66 1 60 1;
+#X connect 67 0 60 0;
+#X connect 68 1 25 0;
diff --git a/doc/help-pdp_mp4player~.pd b/doc/help-pdp_mp4player~.pd
new file mode 100644
index 0000000..d9935cb
--- /dev/null
+++ b/doc/help-pdp_mp4player~.pd
@@ -0,0 +1,17 @@
+#N canvas 259 178 509 391 10;
+#X obj 156 158 dac~;
+#X text 250 113 <-- everything is in this box;
+#X text 279 128 where the block size is redefined;
+#X text 279 143 this is necessary for an;
+#X text 280 155 ( acceptable? ) audio decoding;
+#X obj 395 221 loadbang;
+#X msg 395 251 \; pd dsp 1;
+#X text 51 295 pdp_mp4player~ : decodes a mpeg4ip video stream;
+#X text 51 308 ( from darwin or quicktime server );
+#X obj 128 113 rs_pdp_mp4player~;
+#X text 50 333 The rest is written by Yves Degoyon (ydegoyon@free.fr)
+;
+#X text 51 321 This object uses some code from mpeg4ip;
+#X connect 5 0 6 0;
+#X connect 9 0 0 0;
+#X connect 9 1 0 1;
diff --git a/doc/help-pdp_spotlight.pd b/doc/help-pdp_spotlight.pd
new file mode 100644
index 0000000..bfd1bfd
--- /dev/null
+++ b/doc/help-pdp_spotlight.pd
@@ -0,0 +1,76 @@
+#N canvas 126 7 818 664 10;
+#X obj 221 458 pdp_xv;
+#X obj 268 64 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 123 136 loop \$1;
+#X obj 124 114 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 1
+1;
+#X msg 370 44 open \$1;
+#X obj 369 20 openpanel;
+#X obj 354 3 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 316 99 5 0 0 0 - - -;
+#X msg 225 65 stop;
+#X obj 323 68 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 257 135 metro 70;
+#X floatatom 344 229 5 0 0 0 - - -;
+#X floatatom 358 251 5 0 0 0 - - -;
+#X floatatom 371 272 5 0 0 0 - - -;
+#X obj 252 167 pdp_yqt;
+#X obj 390 163 pdp_v4l;
+#X obj 399 132 metro 70;
+#X obj 444 98 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 401 99 stop;
+#X msg 486 130 open /dev/video;
+#X obj 547 426 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 547 478 pdp_control;
+#X msg 547 451 thread \$1;
+#X floatatom 547 539 5 0 0 0 - - -;
+#X obj 547 510 route pdp_drop;
+#X text 421 271 Spotlight Size;
+#X text 401 250 Y coordinate of spotlight upper left corner;
+#X text 389 227 X coordinate of spotlight upper left corner;
+#X text 100 508 pdp_spotlight : specially made for cabaret and pep'
+;
+#X text 100 522 author : Yves Degoyon ( ydegoyon@free.fr );
+#X floatatom 385 293 5 0 0 0 - - -;
+#X text 435 292 Red component of the color;
+#X floatatom 398 316 5 0 0 0 - - -;
+#X floatatom 409 337 5 0 0 0 - - -;
+#X text 448 315 Green component of the color;
+#X text 459 336 Blue component of the color;
+#X text 424 381 Strength (0<=s<=1) ( default : 0.5 );
+#X obj 429 363 hsl 128 15 0 1 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 220 390 pdp_spotlight;
+#X connect 1 0 10 0;
+#X connect 2 0 14 0;
+#X connect 3 0 2 0;
+#X connect 4 0 14 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 0;
+#X connect 7 0 10 1;
+#X connect 8 0 10 0;
+#X connect 9 0 7 0;
+#X connect 10 0 14 0;
+#X connect 11 0 38 1;
+#X connect 12 0 38 2;
+#X connect 13 0 38 3;
+#X connect 14 0 38 0;
+#X connect 15 0 38 0;
+#X connect 16 0 15 0;
+#X connect 17 0 16 0;
+#X connect 18 0 16 0;
+#X connect 19 0 15 0;
+#X connect 20 0 22 0;
+#X connect 21 0 24 0;
+#X connect 22 0 21 0;
+#X connect 24 0 23 0;
+#X connect 30 0 38 4;
+#X connect 32 0 38 5;
+#X connect 33 0 38 6;
+#X connect 37 0 38 7;
+#X connect 38 0 0 0;
diff --git a/doc/rs_pdp_mp4player~.pd b/doc/rs_pdp_mp4player~.pd
new file mode 100644
index 0000000..4981102
--- /dev/null
+++ b/doc/rs_pdp_mp4player~.pd
@@ -0,0 +1,57 @@
+#N canvas 168 29 842 529 10;
+#X text 460 551 written by Yves Degoyon (ydegoyon@free.fr);
+#X floatatom 226 441 5 0 0 0 - - -;
+#X text 272 440 Streaming status;
+#X floatatom 221 419 5 0 0 0 - - -;
+#X obj 36 449 pdp_xv;
+#X text 321 185 Disconnect from the current stream;
+#X msg 243 183 disconnect;
+#X text 270 420 Number of video frames decoded;
+#X text 457 527 ( at least from ffserver );
+#X text 23 547 NOTE : as for pdp_ffmpeg~ \, transmitting audio;
+#X text 22 565 with the video stream produces some unsteady sound;
+#X text 23 580 a prefered solution would be to use mp3cast~/mp3amp~
+;
+#X obj 70 493 outlet~;
+#X obj 146 486 outlet~;
+#X obj 65 60 block~ 4096;
+#X text 460 513 pdp_live~ : decodes a live video stream;
+#X text 458 539 and reads most common files ( avi \, mpg \, .... )
+;
+#X msg 245 243 audio \$1;
+#X obj 314 245 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X text 345 245 Activate decoding of audio ( default : off );
+#X msg 240 125 connect rtsp://ayp.unia.es/pdstream.sdp;
+#X text 529 126 Connect to a mpeg4ip stream;
+#X msg 244 213 overtcp \$1;
+#X text 358 215 Use RTP over RTSP (TCP);
+#X obj 330 215 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X msg 245 277 priority \$1;
+#X floatatom 334 278 5 0 0 0 - - -;
+#X text 384 299 ( optional \, if you know what you're doing );
+#X text 386 284 ([-20 \, 20 ] default : 0 );
+#X text 389 271 Set the priority of decoding thread;
+#X msg 242 151 connect rtsp://localhost/pdstream.sdp;
+#X obj 114 390 pdp_mp4player~;
+#X msg 243 347 vheight 240;
+#X msg 244 322 vwidth 320;
+#X text 339 324 Set video width ( default : 320 );
+#X text 339 344 Set video height ( default : 240 );
+#X connect 6 0 31 0;
+#X connect 17 0 31 0;
+#X connect 18 0 17 0;
+#X connect 20 0 31 0;
+#X connect 22 0 31 0;
+#X connect 24 0 22 0;
+#X connect 25 0 31 0;
+#X connect 26 0 25 0;
+#X connect 30 0 31 0;
+#X connect 31 0 4 0;
+#X connect 31 1 12 0;
+#X connect 31 2 13 0;
+#X connect 31 3 1 0;
+#X connect 31 4 3 0;
+#X connect 32 0 31 0;
+#X connect 33 0 31 0;
diff --git a/include/pdp_mp4audiosource.h b/include/pdp_mp4audiosource.h
new file mode 100644
index 0000000..ec42c3b
--- /dev/null
+++ b/include/pdp_mp4audiosource.h
@@ -0,0 +1,76 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#ifndef __PDP_MP4AUDIOSOURCE__
+#define __PDP_MP4AUDIOSOURCE__
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/soundcard.h>
+
+#include "media_source.h"
+#include "audio_encoder.h"
+
+class CPDPAudioSource : public CMediaSource {
+ public:
+ CPDPAudioSource(CLiveConfig *pConfig);
+
+ ~CPDPAudioSource() {
+ free(m_pcmFrameBuffer);
+ }
+
+ bool IsDone() {
+ return false;
+ }
+
+ float GetProgress() {
+ return 0.0;
+ }
+
+ void CPDPAudioSource::DoStart();
+
+ void CPDPAudioSource::DoStop();
+
+ void ProcessAudio(u_int8_t* pcmBuffer, u_int32_t pcmBufferSize);
+
+ protected:
+ int ThreadMain();
+
+ bool Init();
+
+
+ protected:
+ int m_maxPasses;
+ Timestamp m_prevTimestamp;
+ int m_audioOssMaxBufferSize;
+ int m_audioOssMaxBufferFrames;
+ Timestamp *m_timestampOverflowArray;
+ size_t m_timestampOverflowArrayIndex;
+ u_int8_t* m_pcmFrameBuffer;
+ u_int32_t m_pcmFrameSize;
+ uint32_t m_channelsConfigured;
+};
+
+
+#endif /* __PDP_MP4AUDIOSOURCE__ */
diff --git a/include/pdp_mp4audiosync.h b/include/pdp_mp4audiosync.h
new file mode 100644
index 0000000..92098d9
--- /dev/null
+++ b/include/pdp_mp4audiosync.h
@@ -0,0 +1,115 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ *
+ * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * audio.h - provides a class that interfaces between the codec and
+ * the SDL audio application. Will provide for volume, buffering,
+ * syncronization
+ */
+
+#ifndef __PDP_MP4AUDIOSYNC__
+#define __PDP_MP4AUDIOSYNC__
+
+#include "audio.h"
+#include "pdp_mp4player~.h"
+
+#define DECODE_BUFFERS_MAX 32
+
+class CPDPAudioSync : public CAudioSync {
+ public:
+ CPDPAudioSync(CPlayerSession *psptr, t_pdp_mp4player *pdp_father);
+ ~CPDPAudioSync(void);
+ // APIs from codec
+ uint8_t *get_audio_buffer(void);
+ void filled_audio_buffer(uint64_t ts, int resync);
+ void set_config(int freq, int channels, int format, uint32_t max_buffer_size);
+ void set_eof(void);
+ void load_audio_buffer(uint8_t *from,
+ uint32_t bytes,
+ uint64_t ts,
+ int resync);
+
+ // APIs from sync task
+ int initialize_audio(int have_video);
+ int is_audio_ready(uint64_t &disptime);
+ uint64_t check_audio_sync(uint64_t current_time, int &have_eof);
+ void play_audio(void);
+ void audio_callback(Uint8 *stream, int len);
+ void flush_sync_buffers(void);
+ void flush_decode_buffers(void);
+
+ // Initialization, other APIs
+ void set_wait_sem(SDL_sem *p) { }; //m_audio_waiting = p; } ;
+ void set_volume(int volume);
+
+ private:
+ void audio_convert_data(void *from, uint32_t len);
+ volatile int m_dont_fill;
+ uint64_t m_buffer_ts;
+ uint32_t m_buffer_offset_on;
+ uint32_t m_buffer_size;
+ uint32_t m_fill_index, m_play_index;
+ volatile int m_buffer_filled[DECODE_BUFFERS_MAX];
+ uint64_t m_buffer_time[DECODE_BUFFERS_MAX];
+ uint64_t m_last_fill_timestamp;
+ uint64_t m_play_time;
+ SDL_AudioSpec m_obtained;
+ uint8_t *m_sample_buffer[DECODE_BUFFERS_MAX];
+ int m_config_set;
+ int m_audio_initialized;
+ int m_freq;
+ int m_channels;
+ int m_format;
+ int m_resync_required;
+ int m_audio_paused;
+ int m_consec_no_buffers;
+ volatile int m_audio_waiting_buffer;
+ int m_use_SDL_delay;
+ uint32_t m_resync_buffer;
+ SDL_sem *m_audio_waiting;
+ uint32_t m_skipped_buffers;
+ uint32_t m_didnt_fill_buffers;
+ int m_first_time;
+ int m_first_filled;
+ uint32_t m_msec_per_frame;
+ uint64_t m_buffer_latency;
+ int m_consec_wrong_latency;
+ int64_t m_wrong_latency_total;
+ int m_volume;
+ int m_do_sync;
+ int m_load_audio_do_next_resync;
+ uint32_t m_sample_size;
+ uint32_t m_play_sample_index;
+ uint32_t m_samples_loaded;
+ uint32_t m_bytes_per_sample;
+ uint64_t m_loaded_next_ts;
+ int m_silence;
+ void *m_convert_buffer;
+ t_pdp_mp4player *m_father;
+};
+
+CPDPAudioSync *pdp_create_audio_sync(CPlayerSession *, t_pdp_mp4player *pdp_father);
+
+#endif
+
+
diff --git a/include/pdp_mp4config.h b/include/pdp_mp4config.h
new file mode 100644
index 0000000..aac8b8b
--- /dev/null
+++ b/include/pdp_mp4config.h
@@ -0,0 +1,381 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#ifndef __LIVE_CONFIG_H__
+#define __LIVE_CONFIG_H__
+
+#include <sys/types.h>
+#include <linux/videodev.h>
+
+#include "pdp_mp4configset.h"
+
+#include "media_time.h"
+#include "video_util_tv.h"
+
+#define FILE_SOURCE "FILE"
+#define URL_SOURCE "URL"
+
+#define AUDIO_SOURCE_OSS "OSS"
+#define AUDIO_SOURCE_PDP "PDP"
+
+#define AUDIO_ENCODER_FAAC "faac"
+#define AUDIO_ENCODER_LAME "lame"
+#define AUDIO_ENCODING_NONE "None"
+#define AUDIO_ENCODING_PCM16 "PCM16"
+#define AUDIO_ENCODING_MP3 "MP3"
+#define AUDIO_ENCODING_AAC "AAC"
+#define AUDIO_ENCODING_AC3 "AC3"
+#define AUDIO_ENCODING_VORBIS "VORBIS"
+
+#define VIDEO_SOURCE_V4L "V4L"
+#define VIDEO_SOURCE_PDP "PDP"
+
+#define VIDEO_ENCODER_FFMPEG "ffmpeg"
+#define VIDEO_ENCODER_DIVX "divx"
+#define VIDEO_ENCODER_H26L "h26l"
+#define VIDEO_ENCODER_XVID "xvid"
+#define VIDEO_ENCODER_H261 "h261"
+
+#define VIDEO_ENCODING_NONE "None"
+#define VIDEO_ENCODING_YUV12 "YUV12"
+#define VIDEO_ENCODING_MPEG2 "MPEG2"
+#define VIDEO_ENCODING_MPEG4 "MPEG4"
+#define VIDEO_ENCODING_H26L "H26L"
+#define VIDEO_ENCODING_H261 "H261"
+
+#define VIDEO_NTSC_FRAME_RATE ((float)29.97)
+#define VIDEO_PAL_FRAME_RATE ((float)25.00)
+
+#define VIDEO_STD_ASPECT_RATIO ((float)1.33) // standard 4:3
+#define VIDEO_LB1_ASPECT_RATIO ((float)2.35) // typical "widescreen" format
+#define VIDEO_LB2_ASPECT_RATIO ((float)1.85) // alternate widescreen format
+#define VIDEO_LB3_ASPECT_RATIO ((float)1.78) // hdtv 16:9
+
+#define MP3_MPEG1_SAMPLES_PER_FRAME 1152 // for MPEG-1 bitrates
+#define MP3_MPEG2_SAMPLES_PER_FRAME 576 // for MPEG-2 bitrates
+
+#define VIDEO_SIGNAL_PAL 0
+#define VIDEO_SIGNAL_NTSC 1
+#define VIDEO_SIGNAL_SECAM 2
+
+DECLARE_CONFIG(CONFIG_APP_REAL_TIME);
+DECLARE_CONFIG(CONFIG_APP_REAL_TIME_SCHEDULER);
+DECLARE_CONFIG(CONFIG_APP_DURATION);
+DECLARE_CONFIG(CONFIG_APP_DURATION_UNITS);
+DECLARE_CONFIG(CONFIG_APP_FILE_0);
+DECLARE_CONFIG(CONFIG_APP_FILE_1);
+DECLARE_CONFIG(CONFIG_APP_FILE_2);
+DECLARE_CONFIG(CONFIG_APP_FILE_3);
+DECLARE_CONFIG(CONFIG_APP_FILE_4);
+DECLARE_CONFIG(CONFIG_APP_FILE_5);
+DECLARE_CONFIG(CONFIG_APP_FILE_6);
+DECLARE_CONFIG(CONFIG_APP_FILE_7);
+DECLARE_CONFIG(CONFIG_APP_DEBUG);
+DECLARE_CONFIG(CONFIG_APP_LOGLEVEL);
+DECLARE_CONFIG(CONFIG_APP_SIGNAL_HALT);
+
+DECLARE_CONFIG(CONFIG_AUDIO_ENABLE);
+DECLARE_CONFIG(CONFIG_AUDIO_SOURCE_TYPE);
+DECLARE_CONFIG(CONFIG_AUDIO_SOURCE_NAME);
+DECLARE_CONFIG(CONFIG_AUDIO_MIXER_NAME);
+DECLARE_CONFIG(CONFIG_AUDIO_INPUT_NAME);
+DECLARE_CONFIG(CONFIG_AUDIO_SOURCE_TRACK);
+DECLARE_CONFIG(CONFIG_AUDIO_CHANNELS);
+DECLARE_CONFIG(CONFIG_AUDIO_SAMPLE_RATE);
+DECLARE_CONFIG(CONFIG_AUDIO_BIT_RATE_KBPS);
+DECLARE_CONFIG(CONFIG_AUDIO_BIT_RATE);
+DECLARE_CONFIG(CONFIG_AUDIO_ENCODING);
+DECLARE_CONFIG(CONFIG_AUDIO_ENCODER);
+DECLARE_CONFIG(CONFIG_AUDIO_OSS_USE_SMALL_FRAGS);
+DECLARE_CONFIG(CONFIG_AUDIO_OSS_FRAGMENTS);
+DECLARE_CONFIG(CONFIG_AUDIO_OSS_FRAG_SIZE);
+
+DECLARE_CONFIG(CONFIG_VIDEO_ENABLE);
+DECLARE_CONFIG(CONFIG_VIDEO_SOURCE_TYPE);
+DECLARE_CONFIG(CONFIG_VIDEO_SOURCE_NAME);
+DECLARE_CONFIG(CONFIG_VIDEO_INPUT);
+DECLARE_CONFIG(CONFIG_VIDEO_SIGNAL);
+DECLARE_CONFIG(CONFIG_VIDEO_TUNER);
+DECLARE_CONFIG(CONFIG_VIDEO_CHANNEL_LIST_INDEX);
+DECLARE_CONFIG(CONFIG_VIDEO_CHANNEL_INDEX);
+DECLARE_CONFIG(CONFIG_VIDEO_SOURCE_TRACK);
+DECLARE_CONFIG(CONFIG_VIDEO_PREVIEW);
+DECLARE_CONFIG(CONFIG_VIDEO_RAW_PREVIEW);
+DECLARE_CONFIG(CONFIG_VIDEO_ENCODED_PREVIEW);
+DECLARE_CONFIG(CONFIG_VIDEO_ENCODER);
+DECLARE_CONFIG(CONFIG_VIDEO_ENCODING);
+DECLARE_CONFIG(CONFIG_VIDEO_RAW_WIDTH);
+DECLARE_CONFIG(CONFIG_VIDEO_RAW_HEIGHT);
+DECLARE_CONFIG(CONFIG_VIDEO_ASPECT_RATIO);
+DECLARE_CONFIG(CONFIG_VIDEO_FRAME_RATE);
+DECLARE_CONFIG(CONFIG_VIDEO_KEY_FRAME_INTERVAL);
+DECLARE_CONFIG(CONFIG_VIDEO_BIT_RATE);
+DECLARE_CONFIG(CONFIG_VIDEO_PROFILE_ID);
+DECLARE_CONFIG(CONFIG_VIDEO_BRIGHTNESS);
+DECLARE_CONFIG(CONFIG_VIDEO_HUE);
+DECLARE_CONFIG(CONFIG_VIDEO_COLOR);
+DECLARE_CONFIG(CONFIG_VIDEO_CONTRAST);
+DECLARE_CONFIG(CONFIG_VIDEO_TIMEBITS);
+DECLARE_CONFIG(CONFIG_V4L_CACHE_TIMESTAMP);
+DECLARE_CONFIG(CONFIG_VIDEO_H261_QUALITY);
+DECLARE_CONFIG(CONFIG_VIDEO_H261_QUALITY_ADJ_FRAMES);
+DECLARE_CONFIG(CONFIG_VIDEO_CAP_BUFF_COUNT);
+
+
+DECLARE_CONFIG(CONFIG_RECORD_ENABLE);
+DECLARE_CONFIG(CONFIG_RECORD_RAW_AUDIO);
+DECLARE_CONFIG(CONFIG_RECORD_RAW_VIDEO);
+DECLARE_CONFIG(CONFIG_RECORD_ENCODED_AUDIO);
+DECLARE_CONFIG(CONFIG_RECORD_ENCODED_VIDEO);
+DECLARE_CONFIG(CONFIG_RECORD_MP4_FILE_NAME);
+DECLARE_CONFIG(CONFIG_RECORD_MP4_HINT_TRACKS);
+DECLARE_CONFIG(CONFIG_RECORD_MP4_OVERWRITE);
+DECLARE_CONFIG(CONFIG_RECORD_MP4_OPTIMIZE);
+
+DECLARE_CONFIG(CONFIG_RTP_ENABLE);
+DECLARE_CONFIG(CONFIG_RTP_DEST_ADDRESS); // for video
+DECLARE_CONFIG(CONFIG_RTP_AUDIO_DEST_PORT);
+DECLARE_CONFIG(CONFIG_RTP_VIDEO_DEST_PORT);
+DECLARE_CONFIG(CONFIG_RTP_RECV_BUFFER_TIME);
+DECLARE_CONFIG(CONFIG_RTP_PAYLOAD_SIZE);
+DECLARE_CONFIG(CONFIG_RTP_MCAST_TTL);
+DECLARE_CONFIG(CONFIG_RTP_DISABLE_TS_OFFSET);
+DECLARE_CONFIG(CONFIG_RTP_USE_SSM);
+DECLARE_CONFIG(CONFIG_SDP_FILE_NAME);
+DECLARE_CONFIG(CONFIG_RTP_AUDIO_DEST_ADDRESS);
+DECLARE_CONFIG(CONFIG_RTP_USE_MP3_PAYLOAD_14);
+DECLARE_CONFIG(CONFIG_RTP_NO_B_RR_0);
+DECLARE_CONFIG(CONFIG_RAW_ENABLE);
+DECLARE_CONFIG(CONFIG_RAW_PCM_FILE_NAME);
+DECLARE_CONFIG(CONFIG_RAW_PCM_FIFO);
+DECLARE_CONFIG(CONFIG_RAW_YUV_FILE_NAME);
+DECLARE_CONFIG(CONFIG_RAW_YUV_FIFO);
+
+
+#ifdef DECLARE_CONFIG_VARIABLES
+static SConfigVariable PdpConfigVariables[] = {
+
+ CONFIG_BOOL(CONFIG_APP_REAL_TIME, "isRealTime", true),
+ CONFIG_BOOL(CONFIG_APP_REAL_TIME_SCHEDULER, "useRealTimeScheduler", true),
+ CONFIG_INT(CONFIG_APP_DURATION, "duration", 1),
+ CONFIG_INT(CONFIG_APP_DURATION_UNITS, "durationUnits", 60),
+
+ CONFIG_STRING(CONFIG_APP_FILE_0, "file0", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_1, "file1", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_2, "file2", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_3, "file3", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_4, "file4", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_5, "file5", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_6, "file6", ""),
+ CONFIG_STRING(CONFIG_APP_FILE_7, "file7", ""),
+
+ CONFIG_BOOL(CONFIG_APP_DEBUG, "debug", false),
+ CONFIG_INT(CONFIG_APP_LOGLEVEL, "logLevel", 0),
+ CONFIG_STRING(CONFIG_APP_SIGNAL_HALT, "signalHalt", "sighup"),
+
+ // AUDIO
+
+ CONFIG_BOOL(CONFIG_AUDIO_ENABLE, "audioEnable", true),
+ CONFIG_STRING(CONFIG_AUDIO_SOURCE_TYPE, "audioSourceType", AUDIO_SOURCE_PDP),
+ CONFIG_STRING(CONFIG_AUDIO_SOURCE_NAME, "audioDevice", "/dev/dsp"),
+ CONFIG_STRING(CONFIG_AUDIO_MIXER_NAME, "audioMixer", "/dev/mixer"),
+ CONFIG_STRING(CONFIG_AUDIO_INPUT_NAME, "audioInput", "mix"),
+
+ CONFIG_INT(CONFIG_AUDIO_SOURCE_TRACK, "audioSourceTrack", 0),
+ CONFIG_INT(CONFIG_AUDIO_CHANNELS, "audioChannels", 2),
+ CONFIG_INT(CONFIG_AUDIO_SAMPLE_RATE, "audioSampleRate", 44100),
+ CONFIG_INT(CONFIG_AUDIO_BIT_RATE_KBPS, "audioBitRate", 128),
+ CONFIG_INT(CONFIG_AUDIO_BIT_RATE, "audioBitRateBps", 128000),
+ CONFIG_STRING(CONFIG_AUDIO_ENCODING, "audioEncoding", AUDIO_ENCODING_AAC),
+ CONFIG_STRING(CONFIG_AUDIO_ENCODER, "audioEncoder", AUDIO_ENCODER_FAAC),
+
+ CONFIG_BOOL(CONFIG_AUDIO_OSS_USE_SMALL_FRAGS, "audioOssUseSmallFrags", true),
+ CONFIG_INT(CONFIG_AUDIO_OSS_FRAGMENTS, "audioOssFragments", 128),
+ CONFIG_INT(CONFIG_AUDIO_OSS_FRAG_SIZE, "audioOssFragSize", 8),
+
+ // VIDEO
+
+ CONFIG_BOOL(CONFIG_VIDEO_ENABLE, "videoEnable", true),
+ CONFIG_STRING(CONFIG_VIDEO_SOURCE_TYPE, "videoSourceType", VIDEO_SOURCE_PDP),
+ CONFIG_STRING(CONFIG_VIDEO_SOURCE_NAME, "videoDevice", "/dev/video0"),
+ CONFIG_INT(CONFIG_VIDEO_INPUT, "videoInput", 1),
+ CONFIG_INT(CONFIG_VIDEO_SIGNAL, "videoSignal", VIDEO_SIGNAL_NTSC),
+
+ CONFIG_INT(CONFIG_VIDEO_TUNER, "videoTuner", -1),
+ CONFIG_INT(CONFIG_VIDEO_CHANNEL_LIST_INDEX, "videoChannelListIndex", 0),
+ CONFIG_INT(CONFIG_VIDEO_CHANNEL_INDEX, "videoChannelIndex", 1),
+
+ CONFIG_INT(CONFIG_VIDEO_SOURCE_TRACK, "videoSourceTrack", 0),
+
+ CONFIG_BOOL(CONFIG_VIDEO_PREVIEW, "videoPreview", true),
+ CONFIG_BOOL(CONFIG_VIDEO_RAW_PREVIEW, "videoRawPreview", false),
+ CONFIG_BOOL(CONFIG_VIDEO_ENCODED_PREVIEW, "videoEncodedPreview", true),
+
+ CONFIG_STRING(CONFIG_VIDEO_ENCODER, "videoEncoder", VIDEO_ENCODER_XVID),
+ CONFIG_STRING(CONFIG_VIDEO_ENCODING, "videoEncoding", VIDEO_ENCODING_MPEG4),
+
+ CONFIG_INT(CONFIG_VIDEO_RAW_WIDTH, "videoRawWidth", 320),
+ CONFIG_INT(CONFIG_VIDEO_RAW_HEIGHT, "videoRawHeight", 240),
+ CONFIG_FLOAT(CONFIG_VIDEO_ASPECT_RATIO, "videoAspectRatio", VIDEO_STD_ASPECT_RATIO),
+ CONFIG_FLOAT(CONFIG_VIDEO_FRAME_RATE, "videoFrameRate", VIDEO_PAL_FRAME_RATE),
+ CONFIG_FLOAT(CONFIG_VIDEO_KEY_FRAME_INTERVAL, "videoKeyFrameInterval", 2.0),
+
+ CONFIG_INT(CONFIG_VIDEO_BIT_RATE, "videoBitRate", 128),
+ CONFIG_INT(CONFIG_VIDEO_PROFILE_ID, "videoProfileId", MPEG4_SP_L3),
+
+ CONFIG_INT(CONFIG_VIDEO_BRIGHTNESS, "videoBrightness", 50),
+ CONFIG_INT(CONFIG_VIDEO_HUE, "videoHue", 50),
+ CONFIG_INT(CONFIG_VIDEO_COLOR, "videoColor", 50),
+ CONFIG_INT(CONFIG_VIDEO_CONTRAST, "videoContrast", 50),
+
+ CONFIG_INT(CONFIG_VIDEO_TIMEBITS, "videoTimebits", 0),
+
+ CONFIG_BOOL(CONFIG_V4L_CACHE_TIMESTAMP, "videoTimestampCache", true),
+ CONFIG_INT(CONFIG_VIDEO_H261_QUALITY, "videoH261Quality", 10),
+ CONFIG_INT(CONFIG_VIDEO_H261_QUALITY_ADJ_FRAMES, "videoH261QualityAdjFrames", 8),
+
+ CONFIG_INT(CONFIG_VIDEO_CAP_BUFF_COUNT, "videoCaptureBuffersCount", 16),
+
+ // RECORD
+ CONFIG_BOOL(CONFIG_RECORD_ENABLE, "recordEnable", true),
+ CONFIG_BOOL(CONFIG_RECORD_RAW_AUDIO, "recordRawAudio", false),
+ CONFIG_BOOL(CONFIG_RECORD_RAW_VIDEO, "recordRawVideo", false),
+ CONFIG_BOOL(CONFIG_RECORD_ENCODED_AUDIO, "recordEncodedAudio", true),
+ CONFIG_BOOL(CONFIG_RECORD_ENCODED_VIDEO, "recordEncodedVideo", true),
+
+ CONFIG_STRING(CONFIG_RECORD_MP4_FILE_NAME, "recordMp4File", "capture.mp4"),
+ CONFIG_BOOL(CONFIG_RECORD_MP4_HINT_TRACKS, "recordMp4HintTracks", true),
+ CONFIG_BOOL(CONFIG_RECORD_MP4_OVERWRITE, "recordMp4Overwrite", true),
+ CONFIG_BOOL(CONFIG_RECORD_MP4_OPTIMIZE, "recordMp4Optimize", false),
+
+ // RTP
+
+ CONFIG_BOOL(CONFIG_RTP_ENABLE, "rtpEnable", true),
+ CONFIG_STRING(CONFIG_RTP_DEST_ADDRESS, "rtpDestAddress", "127.0.0.1"),
+ CONFIG_STRING(CONFIG_RTP_AUDIO_DEST_ADDRESS, "audioRtpDestAddress", "127.0.0.1"),
+
+ CONFIG_INT(CONFIG_RTP_AUDIO_DEST_PORT, "rtpAudioDestPort", 8000),
+ CONFIG_INT(CONFIG_RTP_VIDEO_DEST_PORT, "rtpVideoDestPort", 7070),
+
+ CONFIG_INT(CONFIG_RTP_PAYLOAD_SIZE, "rtpPayloadSize", 1460),
+ CONFIG_INT(CONFIG_RTP_MCAST_TTL, "rtpMulticastTtl", 15),
+
+ CONFIG_BOOL(CONFIG_RTP_DISABLE_TS_OFFSET, "rtpDisableTimestampOffset", false),
+ CONFIG_BOOL(CONFIG_RTP_USE_SSM, "rtpUseSingleSourceMulticast", false),
+
+ CONFIG_STRING(CONFIG_SDP_FILE_NAME, "sdpFile", "capture.sdp"),
+
+ CONFIG_BOOL(CONFIG_RTP_USE_MP3_PAYLOAD_14, "rtpUseMp4RtpPayload14", false),
+ CONFIG_BOOL(CONFIG_RTP_NO_B_RR_0, "rtpNoBRR0", false),
+
+ // RAW sink
+
+ CONFIG_BOOL(CONFIG_RAW_ENABLE, "rawEnable", false),
+ CONFIG_STRING(CONFIG_RAW_PCM_FILE_NAME, "rawAudioFile", "capture.pcm"),
+ CONFIG_BOOL(CONFIG_RAW_PCM_FIFO, "rawAudioUseFifo", false),
+
+ CONFIG_STRING(CONFIG_RAW_YUV_FILE_NAME, "rawVideoFile", "capture.yuv"),
+ CONFIG_BOOL(CONFIG_RAW_YUV_FIFO, "rawVideoUseFifo", false)
+
+};
+#endif
+
+// forward declarations
+class CVideoCapabilities;
+class CAudioCapabilities;
+class CLiveConfig;
+
+// some configuration utility routines
+void GenerateMpeg4VideoConfig(CLiveConfig* pConfig);
+bool GenerateSdpFile(CLiveConfig* pConfig);
+struct session_desc_t;
+
+session_desc_t *createSdpDescription(CLiveConfig *pConfig,
+ char *sAudioDestAddr,
+ char *sVideoDestAddr,
+ int ttl,
+ bool allow_rtcp,
+ int video_port,
+ int audio_port);
+
+class CLiveConfig : public CConfigSet {
+public:
+ CLiveConfig(SConfigVariable* variables,
+ config_index_t numVariables, const char* defaultFileName);
+
+ ~CLiveConfig();
+
+ // recalculate derived values
+ void Update();
+ void UpdateFileHistory(const char* fileName);
+ void UpdateVideo();
+ void CalculateVideoFrameSize();
+ void UpdateAudio();
+ void UpdateRecord();
+
+ bool IsOneSource();
+ bool IsCaptureVideoSource();
+ bool IsCaptureAudioSource();
+ bool IsFileVideoSource();
+ bool IsFileAudioSource();
+
+ bool SourceRawVideo() {
+ return false;
+ }
+
+ bool SourceRawAudio() {
+ return false;
+ }
+
+public:
+ // command line configuration
+ bool m_appAutomatic;
+
+ // derived, shared video configuration
+ CVideoCapabilities* m_videoCapabilities;
+ bool m_videoEncode;
+ u_int32_t m_videoPreviewWindowId;
+ u_int16_t m_videoWidth;
+ u_int16_t m_videoHeight;
+ u_int16_t m_videoMaxWidth;
+ u_int16_t m_videoMaxHeight;
+ u_int32_t m_ySize;
+ u_int32_t m_uvSize;
+ u_int32_t m_yuvSize;
+ bool m_videoNeedRgbToYuv;
+ u_int16_t m_videoMpeg4ConfigLength;
+ u_int8_t* m_videoMpeg4Config;
+ u_int32_t m_videoMaxVopSize;
+ u_int8_t m_videoTimeIncrBits;
+
+ // derived, shared audio configuration
+ CAudioCapabilities* m_audioCapabilities;
+ bool m_audioEncode;
+
+ // derived, shared file configuration
+ u_int64_t m_recordEstFileSize;
+};
+
+#endif /* __LIVE_CONFIG_H__ */
+
diff --git a/include/pdp_mp4configset.h b/include/pdp_mp4configset.h
new file mode 100644
index 0000000..a20d260
--- /dev/null
+++ b/include/pdp_mp4configset.h
@@ -0,0 +1,532 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#ifndef __CONFIG_SET_H__
+#define __CONFIG_SET_H__
+
+#include <mpeg4ip.h>
+
+#ifndef CONFIG_SAFETY
+#define CONFIG_SAFETY 1
+#endif
+
+typedef u_int32_t config_integer_t;
+
+typedef u_int16_t config_index_t;
+
+
+enum ConfigException {
+ CONFIG_ERR_INAME,
+ CONFIG_ERR_TYPE,
+ CONFIG_ERR_MEMORY,
+};
+
+// TBD type specific exception info and printing utility
+class CConfigException {
+public:
+ CConfigException(ConfigException e) {
+ type = e;
+ fprintf( stderr, "pdp_mp4configset : exception : type : %d\n", e );
+ }
+ ConfigException type;
+};
+
+#define CONFIG_MAX_STRLEN 255
+
+// TBD weld this in, and throw exception
+inline char* stralloc(const char* src) {
+ char* dst = (char*)malloc(strlen(src)+1);
+ if (dst) {
+ strcpy(dst, src);
+ }
+ return dst;
+}
+
+enum ConfigType {
+ CONFIG_TYPE_UNDEFINED,
+ CONFIG_TYPE_INTEGER,
+ CONFIG_TYPE_BOOL,
+ CONFIG_TYPE_STRING,
+ CONFIG_TYPE_FLOAT
+};
+
+union UConfigValue {
+ UConfigValue(void) {
+ m_svalue = NULL;
+ }
+ UConfigValue(config_integer_t ivalue) {
+ m_ivalue = ivalue;
+ }
+ UConfigValue(bool bvalue) {
+ m_bvalue = bvalue;
+ }
+ UConfigValue(char* svalue) {
+ m_svalue = svalue;
+ }
+ UConfigValue(float fvalue) {
+ m_fvalue = fvalue;
+ }
+
+ config_integer_t m_ivalue;
+ bool m_bvalue;
+ char* m_svalue;
+ float m_fvalue;
+};
+
+struct SConfigVariable {
+ config_index_t *m_iName;
+ const char* m_sName;
+ ConfigType m_type;
+ UConfigValue m_defaultValue;
+ UConfigValue m_value;
+
+ const char* ToAscii() {
+ static char sBuf[CONFIG_MAX_STRLEN+3];
+ switch (m_type) {
+ case CONFIG_TYPE_INTEGER:
+ sprintf(sBuf, "%d", m_value.m_ivalue);
+ return sBuf;
+ case CONFIG_TYPE_BOOL:
+ sprintf(sBuf, "%d", m_value.m_bvalue);
+ return sBuf;
+ case CONFIG_TYPE_STRING:
+ if (strchr(m_value.m_svalue, ' ')) {
+ sBuf[0] = '"';
+ strncpy(&sBuf[1], m_value.m_svalue, CONFIG_MAX_STRLEN);
+ strcpy(&sBuf[
+ MIN(strlen(m_value.m_svalue), CONFIG_MAX_STRLEN)+1], "\"");
+ }
+ return m_value.m_svalue;
+ case CONFIG_TYPE_FLOAT:
+ sprintf(sBuf, "%f", m_value.m_fvalue);
+ return sBuf;
+ default:
+ return "";
+ }
+ }
+
+ bool FromAscii(const char* s) {
+ switch (m_type) {
+ case CONFIG_TYPE_INTEGER:
+ return (sscanf(s, " %i ", &m_value.m_ivalue) == 1);
+ case CONFIG_TYPE_BOOL:
+ // OPTION could add "yes/no", "true/false"
+ if (sscanf(s, " %u ", &m_value.m_ivalue) != 1) {
+ return false;
+ }
+ m_value.m_bvalue = m_value.m_ivalue ? true : false;
+ return true;
+ case CONFIG_TYPE_STRING:
+ // N.B. assuming m_svalue has been alloc'ed
+ {
+ size_t len = strlen(s);
+ free(m_value.m_svalue);
+ if (*s == '"' && s[len] == '"') {
+ m_value.m_svalue = strdup(s + 1);
+ m_value.m_svalue[len - 1] = '\0';
+ } else {
+ m_value.m_svalue = strdup(s);
+ }
+ if (m_value.m_svalue == NULL) {
+ throw new CConfigException(CONFIG_ERR_MEMORY);
+ }
+ return true;
+ }
+ case CONFIG_TYPE_FLOAT:
+ return (sscanf(s, " %f ", &m_value.m_fvalue) == 1);
+ default:
+ return false;
+ }
+ }
+
+ void SetToDefault(void) {
+ switch (m_type) {
+ case CONFIG_TYPE_INTEGER:
+ m_value.m_ivalue = m_defaultValue.m_ivalue;
+ break;
+ case CONFIG_TYPE_BOOL:
+ m_value.m_bvalue = m_defaultValue.m_bvalue;
+ break;
+ case CONFIG_TYPE_STRING:
+ // free(m_value.m_svalue);
+ m_value.m_svalue = stralloc(m_defaultValue.m_svalue);
+ if (m_value.m_svalue == NULL) {
+ throw new CConfigException(CONFIG_ERR_MEMORY);
+ }
+ break;
+ case CONFIG_TYPE_FLOAT:
+ m_value.m_fvalue = m_defaultValue.m_fvalue;
+ break;
+ default:
+ break;
+ }
+ }
+
+ bool IsValueDefault(void) {
+ switch (m_type) {
+ case CONFIG_TYPE_INTEGER:
+ return m_value.m_ivalue == m_defaultValue.m_ivalue;
+ case CONFIG_TYPE_BOOL:
+ return m_value.m_bvalue == m_defaultValue.m_bvalue;
+ case CONFIG_TYPE_STRING:
+ return (strcmp(m_value.m_svalue, m_defaultValue.m_svalue) == 0);
+ case CONFIG_TYPE_FLOAT:
+ return m_value.m_fvalue == m_defaultValue.m_fvalue;
+ default:
+ return false;
+ }
+ }
+ void CleanUpConfig(void) {
+ if (m_type == CONFIG_TYPE_STRING) {
+ CHECK_AND_FREE(m_value.m_svalue);
+ }
+ }
+};
+
+struct SUnknownConfigVariable {
+ struct SUnknownConfigVariable *next;
+ char *value;
+};
+
+class CConfigSet {
+public:
+ CConfigSet(SConfigVariable* variables,
+ config_index_t numVariables,
+ const char* defaultFileName) {
+ uint32_t size;
+ m_fileName = NULL;
+ m_debug = false;
+ m_variables = variables;
+ m_numVariables = numVariables;
+ size = sizeof(SConfigVariable) * numVariables;
+ m_variables =
+ (SConfigVariable*)malloc(size);
+
+ memcpy(m_variables, variables, size);
+ m_defaultFileName = strdup(defaultFileName);
+ SetToDefaults();
+ m_unknown_head = NULL;
+ };
+
+ ~CConfigSet() {
+ free(m_fileName);
+ for (config_index_t i = 0; i < m_numVariables; i++) {
+ m_variables[i].CleanUpConfig();
+ }
+ free(m_variables);
+ m_variables = NULL;
+ SUnknownConfigVariable *ptr = m_unknown_head;
+ while (ptr != NULL) {
+ m_unknown_head = ptr->next;
+ free(ptr->value);
+ free(ptr);
+ ptr = m_unknown_head;
+ }
+ CHECK_AND_FREE(m_defaultFileName);
+ }
+
+ void InitializeIndexes(void) {
+ for (config_index_t ix = 0; ix < m_numVariables; ix++) {
+ *m_variables[ix].m_iName = ix;
+ }
+ }
+
+ void AddConfigVariables (SConfigVariable* vars,
+ config_index_t numVariables) {
+ config_index_t start = m_numVariables;
+ uint32_t size = sizeof(SConfigVariable) *
+ (m_numVariables + numVariables);
+ m_variables = (SConfigVariable*)realloc(m_variables, size);
+ memcpy(&m_variables[m_numVariables], vars,
+ numVariables * sizeof(SConfigVariable));
+ m_numVariables += numVariables;
+ SetToDefaults(start);
+ }
+
+ const char* GetFileName() {
+ return m_fileName;
+ }
+
+ inline void CheckIName(config_index_t iName) {
+ if (iName >= m_numVariables) {
+ throw new CConfigException(CONFIG_ERR_INAME);
+ }
+ if (*m_variables[iName].m_iName != iName) {
+ throw new CConfigException(CONFIG_ERR_INAME);
+ }
+ }
+
+ inline void CheckIntegerType(config_index_t iName) {
+ if (m_variables[iName].m_type != CONFIG_TYPE_INTEGER) {
+ throw new CConfigException(CONFIG_ERR_TYPE);
+ }
+ }
+
+ inline void CheckBoolType(config_index_t iName) {
+ if (m_variables[iName].m_type != CONFIG_TYPE_BOOL) {
+ throw new CConfigException(CONFIG_ERR_TYPE);
+ }
+ }
+
+ inline void CheckStringType(config_index_t iName) {
+ if (m_variables[iName].m_type != CONFIG_TYPE_STRING) {
+ throw new CConfigException(CONFIG_ERR_TYPE);
+ }
+ }
+
+ inline void CheckFloatType(config_index_t iName) {
+ if (m_variables[iName].m_type != CONFIG_TYPE_FLOAT) {
+ throw new CConfigException(CONFIG_ERR_TYPE);
+ }
+ }
+
+ inline bool IsDefault (const config_index_t iName) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckIntegerType(iName);
+#endif
+ return m_variables[iName].IsValueDefault();
+ };
+
+ inline config_integer_t GetIntegerValue(const config_index_t iName) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckIntegerType(iName);
+#endif
+ return m_variables[iName].m_value.m_ivalue;
+ }
+
+ inline void SetIntegerValue(const config_index_t iName,
+ config_integer_t ivalue) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckIntegerType(iName);
+#endif
+ m_variables[iName].m_value.m_ivalue = ivalue;
+ }
+
+ inline bool GetBoolValue(const config_index_t iName) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckBoolType(iName);
+#endif
+ return m_variables[iName].m_value.m_bvalue;;
+ }
+
+ inline void SetBoolValue(const config_index_t iName, bool bvalue) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckBoolType(iName);
+#endif
+ m_variables[iName].m_value.m_bvalue = bvalue;
+ }
+
+ inline char* GetStringValue(const config_index_t iName) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckStringType(iName);
+#endif
+ return m_variables[iName].m_value.m_svalue;
+ }
+
+ inline void SetStringValue(const config_index_t iName, const char* svalue) {
+ printf ( "setting variable : %d to : %s\n", iName, svalue );
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckStringType(iName);
+#endif
+ if (svalue == m_variables[iName].m_value.m_svalue) {
+ return;
+ }
+ // free(m_variables[iName].m_value.m_svalue);
+ m_variables[iName].m_value.m_svalue = stralloc(svalue);
+ if (m_variables[iName].m_value.m_svalue == NULL) {
+ throw new CConfigException(CONFIG_ERR_MEMORY);
+ }
+ }
+
+ inline float GetFloatValue(const config_index_t iName) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckFloatType(iName);
+#endif
+ return m_variables[iName].m_value.m_fvalue;
+ }
+
+ inline void SetFloatValue(const config_index_t iName, float fvalue) {
+#if CONFIG_SAFETY
+ CheckIName(iName);
+ CheckFloatType(iName);
+#endif
+ m_variables[iName].m_value.m_fvalue = fvalue;
+ }
+
+ void SetToDefaults(int start = 0) {
+ for (config_index_t i = start; i < m_numVariables; i++) {
+ m_variables[i].SetToDefault();
+ }
+ }
+
+ void SetToDefault(const config_index_t iName) {
+ m_variables[iName].SetToDefault();
+ }
+
+ void ProcessLine (char *line) {
+ // comment
+ if (line[0] == '#') {
+ return;
+ }
+ char* s = line;
+ while (*s != '\0') s++;
+ s--;
+ while (isspace(*s)) {
+ *s = '\0';
+ s--;
+ }
+ s = line;
+
+ SConfigVariable* var = FindByName(strsep(&s, "="));
+ if (var == NULL || s == NULL) {
+ if (s != NULL) {
+ *(s - 1) = '='; // restore seperation character
+ SUnknownConfigVariable *ptr;
+ ptr = MALLOC_STRUCTURE(SUnknownConfigVariable);
+ ptr->next = m_unknown_head;
+ ptr->value = strdup(line);
+ m_unknown_head = ptr;
+ }
+ if (m_debug) {
+ fprintf(stderr, "bad config line %s\n", s);
+ }
+ return;
+ }
+ if (!var->FromAscii(s)) {
+ if (m_debug) {
+ fprintf(stderr, "bad config value in line %s\n", s);
+ }
+ }
+ }
+
+ bool ReadFromFile(const char* fileName) {
+ free(m_fileName);
+ m_fileName = stralloc(fileName);
+ FILE* pFile = fopen(fileName, "r");
+ if (pFile == NULL) {
+ if (m_debug) {
+ fprintf(stderr, "couldn't open file %s\n", fileName);
+ }
+ return false;
+ }
+ char line[256];
+ while (fgets(line, sizeof(line), pFile)) {
+ ProcessLine(line);
+ }
+ fclose(pFile);
+ return true;
+ }
+
+ bool WriteToFile(const char* fileName, bool allValues = false) {
+ FILE* pFile = fopen(fileName, "w");
+ config_index_t i;
+ SConfigVariable *var;
+ SUnknownConfigVariable *ptr;
+
+ if (pFile == NULL) {
+ if (m_debug) {
+ fprintf(stderr, "couldn't open file %s\n", fileName);
+ }
+ return false;
+ }
+ for (i = 0; i < m_numVariables; i++) {
+ var = &m_variables[i];
+ if (allValues || !var->IsValueDefault()) {
+ fprintf(pFile, "%s=%s\n", var->m_sName, var->ToAscii());
+ }
+ }
+ ptr = m_unknown_head;
+ while (ptr != NULL) {
+ fprintf(pFile, "%s\n", ptr->value);
+ ptr = ptr->next;
+ }
+ fclose(pFile);
+ return true;
+ }
+
+ bool ReadDefaultFile(void) {
+ return ReadFromFile(m_defaultFileName);
+ }
+ bool WriteDefaultFile(void) {
+ return WriteToFile(m_defaultFileName);
+ }
+
+ void SetDebug(bool debug = true) {
+ m_debug = debug;
+ }
+
+protected:
+ SConfigVariable* FindByName(const char* sName) {
+ for (config_index_t i = 0; i < m_numVariables; i++) {
+ if (!strcasecmp(sName, m_variables[i].m_sName)) {
+ return &m_variables[i];
+ }
+ }
+ return NULL;
+ };
+
+protected:
+ SConfigVariable* m_variables;
+ config_index_t m_numVariables;
+ const char* m_defaultFileName;
+ bool m_debug;
+ char* m_fileName;
+ SUnknownConfigVariable *m_unknown_head;
+};
+
+// To define configuration variables - first DECLARE_CONFIG in a
+// .h file. Then in either a C++ or h file, define a static array
+// of configuration variables using CONFIG_BOOL, CONFIG_FLOAT, CONFIG_INT
+// or CONFIG_STRING. You can include the .h anywhere you use the variable -
+// in a .cpp, you must include the .h file with DECLARE_CONFIG_VARIABLES
+// defined before the .h file. Note - if you're already including mp4live.h,
+// you need to #define the DECLARE_CONFIG_VARIABLES after the include.
+//
+// Note - you want to add the config variables BEFORE the ReadFromFile
+// call
+#ifdef DECLARE_CONFIG_VARIABLES
+#define DECLARE_CONFIG(a) config_index_t (a);
+#else
+#define DECLARE_CONFIG(a) extern config_index_t (a);
+#endif
+
+#define CONFIG_BOOL(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_BOOL, (defval), }
+#define CONFIG_FLOAT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_FLOAT,(float) (defval), }
+#define CONFIG_INT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_INTEGER,(config_integer_t) (defval), }
+#define CONFIG_STRING(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_STRING, (defval), }
+
+
+#endif /* __CONFIG_SET_H__ */
diff --git a/include/pdp_mp4playermedia.h b/include/pdp_mp4playermedia.h
new file mode 100644
index 0000000..025fb57
--- /dev/null
+++ b/include/pdp_mp4playermedia.h
@@ -0,0 +1,232 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ *
+ * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * player_media.h - provides CPlayerMedia class, which defines the
+ * interface to a particular media steam.
+ */
+
+#ifndef __PLAYER_MEDIA_H__
+#define __PLAYER_MEDIA_H__
+
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <sdp/sdp.h>
+#include <rtsp/rtsp_client.h>
+#include <rtp/rtp.h>
+#include "our_bytestream.h"
+#include "our_msg_queue.h"
+#include "codec_plugin.h"
+
+class CPlayerSession;
+class CPDPAudioSync;
+class CPDPVideoSync;
+class C2ConsecIpPort;
+class COurInByteStream;
+class CRtpByteStreamBase;
+
+class CPlayerMedia {
+ public:
+ CPlayerMedia(CPlayerSession *p);
+ ~CPlayerMedia();
+ /* API routine - create - for RTP stream */
+ int create_streaming(media_desc_t *sdp_media,
+ char *errmsg,
+ uint32_t errlen,
+ int on_demand,
+ int use_rtsp,
+ int media_number_in_session);
+ /* API routine - create - where we provide the bytestream */
+ int create(COurInByteStream *b,
+ int is_video,
+ char *errmsg = NULL,
+ uint32_t errlen = 0,
+ int streaming = 0);
+ /* API routine - play, pause */
+ int do_play(double start_time_offset, char *errmsg, uint32_t errlen);
+ int do_pause(void);
+ int is_video(void) { return (m_is_video); };
+ double get_max_playtime(void);
+ /* API routine - interface for decoding start/continue */
+ void start_decoding(void);
+ void bytestream_primed(void);
+ /* API routine - ip port information */
+ uint16_t get_our_port (void) { return m_our_port; };
+ void set_server_port (uint16_t port) { m_server_port = port; };
+ uint16_t get_server_port (void) { return m_server_port; };
+
+ media_desc_t *get_sdp_media_desc (void) { return m_media_info; };
+ void set_source_addr (char *s)
+ {
+ if (m_source_addr != NULL) free(m_source_addr);
+ m_source_addr = s;
+ }
+ const char *get_source_addr(void);
+ CPlayerMedia *get_next (void) { return m_next; };
+ void set_next (CPlayerMedia *newone) { m_next = newone; };
+ int decode_thread(void);
+
+ /* Public RTP routines - receive thread, callback, and routines to
+ * pass information from rtsp to rtp byte stream
+ */
+ int recv_thread(void);
+ void recv_callback(struct rtp *session, rtp_event *e);
+ void set_rtp_ssrc (uint32_t ssrc)
+ { m_rtp_ssrc = ssrc; m_rtp_ssrc_set = TRUE;};
+ void set_rtp_base_ts(uint32_t time);
+ void set_rtp_base_seq(uint16_t seq);
+
+ void set_video_sync(CPDPVideoSync *p) {m_video_sync = p;};
+ void set_audio_sync(CPDPAudioSync *p) {m_audio_sync = p;};
+
+ const video_info_t *get_video_info (void) { return m_video_info; };
+ const audio_info_t *get_audio_info (void) { return m_audio_info; };
+
+ int create_video_plugin(const codec_plugin_t *p,
+ const char *compressor,
+ int profile,
+ int type,
+ format_list_t *sdp_media,
+ video_info_t *video,
+ const uint8_t *user_data,
+ uint32_t userdata_size);
+ int create_audio_plugin(const codec_plugin_t *p,
+ const char *compressor,
+ int profile,
+ int type,
+ format_list_t *sdp_media,
+ audio_info_t *audio,
+ const uint8_t *user_data,
+ uint32_t userdata_size);
+ void set_plugin_data (const codec_plugin_t *p,
+ codec_data_t *d,
+ video_vft_t *v,
+ audio_vft_t *a);
+ int get_plugin_status(char *buffer, uint32_t buflen);
+ void set_user_data (const uint8_t *udata, int length) {
+ m_user_data = udata;
+ m_user_data_size = length;
+ }
+ rtsp_session_t *get_rtsp_session(void) { return m_rtsp_session; };
+ void rtp_init_tcp(void);
+ void rtp_periodic(void);
+ void rtp_start(void);
+ void rtp_end(void);
+ int rtp_receive_packet(unsigned char interleaved, struct rtp_packet *, int len);
+ int rtcp_send_packet(uint8_t *buffer, int buflen);
+ int get_rtp_media_number (void) { return m_rtp_media_number_in_session; };
+ void syncronize_rtp_bytestreams(rtcp_sync_t *sync);
+ private:
+ int create_common(int is_video, char *errmsg, uint32_t errlen);
+ void wait_on_bytestream(void);
+ int m_streaming;
+ int m_is_video;
+ int m_paused;
+ CPlayerMedia *m_next;
+ CPlayerSession *m_parent;
+ media_desc_t *m_media_info;
+ format_list_t *m_media_fmt; // format currently running.
+ rtsp_session_t *m_rtsp_session;
+ C2ConsecIpPort *m_ports;
+ in_port_t m_our_port;
+ in_port_t m_server_port;
+ char *m_source_addr;
+
+ time_t m_start_time;
+ int m_stream_ondemand;
+ int m_sync_time_set;
+ uint64_t m_sync_time_offset;
+ uint32_t m_rtptime_tickpersec;
+ double m_play_start_time;
+ // Receive thread variables
+ SDL_Thread *m_recv_thread;
+
+ /*************************************************************************
+ * RTP variables - used to pass info to the bytestream
+ *************************************************************************/
+ int m_rtp_ondemand;
+ int m_rtp_use_rtsp;
+ int m_rtp_media_number_in_session;
+ int m_rtp_buffering;
+ struct rtp *m_rtp_session;
+ CRtpByteStreamBase *m_rtp_byte_stream;
+ CMsgQueue m_rtp_msg_queue;
+
+ rtp_packet *m_head, *m_tail;
+ uint32_t m_rtp_queue_len;
+
+ // from rtsp...
+ int m_rtp_ssrc_set;
+ uint32_t m_rtp_ssrc;
+ int m_rtsp_base_ts_received;
+ uint32_t m_rtp_base_ts;
+ int m_rtsp_base_seq_received;
+ uint16_t m_rtp_base_seq;
+
+ int determine_payload_type_from_rtp(void);
+ void create_rtp_byte_stream(uint8_t payload, uint64_t tps, format_list_t *fmt);
+ void clear_rtp_packets(void);
+
+ // from rtcp, for broadcast, in case we get an RTCP before we determine
+ // the payload type
+ uint32_t m_rtcp_ntp_frac;
+ uint32_t m_rtcp_ntp_sec;
+ uint32_t m_rtcp_rtp_ts;
+ int m_rtcp_received;
+
+ volatile int m_rtp_inited;
+
+ /*************************************************************************
+ * Decoder thread variables
+ *************************************************************************/
+ SDL_Thread *m_decode_thread;
+ volatile int m_decode_thread_waiting;
+ SDL_sem *m_decode_thread_sem;
+
+ const codec_plugin_t *m_plugin;
+ codec_data_t *m_plugin_data;
+
+ // State change variable
+ CMsgQueue m_decode_msg_queue;
+ // Private routines
+ int process_rtsp_transport(char *transport);
+ CPDPAudioSync *m_audio_sync;
+ CPDPVideoSync *m_video_sync;
+ void parse_decode_message(int &thread_stop, int &decoding);
+ COurInByteStream *m_byte_stream;
+ video_info_t *m_video_info;
+ audio_info_t *m_audio_info;
+
+ const uint8_t *m_user_data;
+ int m_user_data_size;
+
+};
+
+int pdp_process_rtsp_rtpinfo(char *rtpinfo, CPlayerSession *session, CPlayerMedia *media);
+
+extern audio_vft_t audio_vft;
+extern video_vft_t video_vft;
+
+#define media_message(loglevel, fmt...) message(loglevel, "media", fmt)
+
+#endif
diff --git a/include/pdp_mp4playersession.h b/include/pdp_mp4playersession.h
new file mode 100644
index 0000000..5979aad
--- /dev/null
+++ b/include/pdp_mp4playersession.h
@@ -0,0 +1,249 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ * video aspect ratio by:
+ * Peter Maersk-Moller peter @maersk-moller.net
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ *
+ */
+
+/*
+ * pdp_mp4playersession.h - provides definitions for a CPlayerSession.
+ * CPlayerSession is the base class that provides a combination audio/video
+ * stream/file playback.
+ * This class should be the main interface between any outside functionality
+ * and the player window.
+ */
+
+
+#ifndef __PDP_MP4PLAYERSESSION_H__
+#define __PDP_MP4PLAYERSESSION_H__
+
+struct rtcp_sync_t;
+typedef struct rtcp_sync_t rtcp_sync_t;
+
+#include <rtsp/rtsp_client.h>
+#include <sdp/sdp.h>
+#include "our_msg_queue.h"
+#include "ip_port.h"
+#include "pdp_mp4player~.h"
+
+class CPlayerMedia;
+class CPDPAudioSync;
+class CPDPVideoSync;
+
+typedef enum {
+ SESSION_PAUSED,
+ SESSION_BUFFERING,
+ SESSION_PLAYING,
+ SESSION_DONE
+} session_state_t;
+
+typedef struct rtcp_sync_t {
+ uint64_t first_pak_ts;
+ uint64_t rtcp_ts;
+ uint32_t first_pak_rtp_ts;
+ uint32_t rtcp_rtp_ts;
+ uint64_t timescale;
+} rtcp_sync_t;
+
+typedef void (*media_close_callback_f)(void *);
+
+class CPlayerSession {
+ public:
+ /*
+ * API routine - create player session.
+ */
+ CPlayerSession(CMsgQueue *master_queue,
+ SDL_sem *master_sem,
+ const char *name,
+ t_pdp_mp4player *pdp_father);
+ /*
+ * API routine - destroy session - free all sub-structures, cleans
+ * up rtsp, etc
+ */
+ ~CPlayerSession();
+ /*
+ * API routine - create a rtsp session with the url. After that, you
+ * need to associate media
+ */
+ int create_streaming_broadcast(session_desc_t *sdp,
+ char *ermsg,
+ uint32_t errlen);
+ int create_streaming_ondemand(const char *url,
+ char *errmsg,
+ uint32_t errlen,
+ int use_rtp_tcp);
+ /*
+ * API routine - play at time. If start_from_begin is FALSE, start_time
+ * and we're paused, it will continue from where it left off.
+ */
+ int play_all_media(int start_from_begin = FALSE, double start_time = 0.0,
+ char *errmsg = NULL, uint32_t errlen = 0);
+ /*
+ * API routine - pause
+ */
+ int pause_all_media(void);
+ /*
+ * API routine for media set up - associate a created
+ * media with the session.
+ */
+ void add_media(CPlayerMedia *m);
+ /*
+ * API routine - returns sdp info for streamed session
+ */
+ session_desc_t *get_sdp_info (void) { return m_sdp_info;} ;
+ rtsp_client_t *get_rtsp_client (void) { return m_rtsp_client; };
+ /*
+ * API routine - after setting up media, need to set up sync thread
+ */
+ void set_up_sync_thread(void);
+ CPDPVideoSync *set_up_video_sync(void);
+ CPDPAudioSync *set_up_audio_sync(void);
+ /*
+ * API routine - get the current time
+ */
+ uint64_t get_playing_time (void) {
+ if (m_streaming && session_is_seekable() == 0) {
+ return (m_current_time - m_first_time_played);
+ }
+ return (m_current_time);
+ };
+ /*
+ * API routine - get max play time
+ */
+ double get_max_time (void);
+ /*
+ * Other API routines
+ */
+ int session_has_audio(void);
+ int session_has_video(void);
+ void set_audio_volume(int volume);
+ int get_audio_volume(void) { return m_audio_volume; };
+ void session_set_seekable (int seekable) {
+ m_seekable = seekable;
+ };
+ int session_is_seekable (void) {
+ return (m_seekable);
+ };
+ session_state_t get_session_state(void) {
+ return (m_session_state);
+ }
+ void set_media_close_callback (media_close_callback_f mccf,
+ void *mccd) {
+ m_media_close_callback = mccf;
+ m_media_close_callback_data = mccd;
+ }
+ int session_is_network (int &on_demand, int &rtp_over_rtsp) {
+ if (m_streaming == 0) {
+ return 0;
+ }
+ if (m_seekable) {
+ on_demand = 1;
+ rtp_over_rtsp = m_rtp_over_rtsp;
+ } else {
+ on_demand = 0;
+ rtp_over_rtsp = 0;
+ }
+ return 1;
+ }
+ /*
+ * Non-API routines - used for c interfaces, for sync task APIs.
+ */
+ void wake_sync_thread (void) {
+ SDL_SemPost(m_sync_sem);
+ }
+ int send_sync_thread_a_message(uint32_t msgval,
+ unsigned char *msg = NULL,
+ uint32_t msg_len = 0)
+ {
+ return (m_sync_thread_msg_queue.send_message(msgval, msg, msg_len, m_sync_sem));
+ };
+ int sync_thread(int state);
+ uint64_t get_current_time(void);
+ void audio_is_ready (uint64_t latency, uint64_t time);
+ void adjust_start_time(int64_t delta);
+ int session_control_is_aggregate (void) {
+ return m_session_control_is_aggregate;
+ };
+ void set_session_control (int is_aggregate) {
+ m_session_control_is_aggregate = is_aggregate;
+ }
+ CPlayerMedia *rtsp_url_to_media (const char *url);
+ int set_session_desc(int line, const char *desc);
+ const char *get_session_desc(int line);
+ void streaming_media_set_up(void) { m_streaming_media_set_up = 1; };
+ CIpPort **get_unused_ip_port_ptr(void) { return &m_unused_ports; };
+ void syncronize_rtp_bytestreams(rtcp_sync_t *sync);
+ private:
+ int process_msg_queue(int state);
+ int sync_thread_init(void);
+ int sync_thread_wait_sync(void);
+ int sync_thread_wait_audio(void);
+ int sync_thread_playing(void);
+ int sync_thread_paused(void);
+ int sync_thread_done(void);
+ const char *m_session_name;
+ const char *m_content_base;
+ int m_paused;
+ int m_streaming;
+ uint64_t m_current_time; // current time playing
+ uint64_t m_start;
+ uint64_t m_latency;
+ int m_clock_wrapped;
+ uint64_t m_play_start_time;
+ session_desc_t *m_sdp_info;
+ rtsp_client_t *m_rtsp_client;
+ CPlayerMedia *m_my_media;
+ CPDPAudioSync *m_audio_sync;
+ CPDPVideoSync *m_video_sync;
+ SDL_Thread *m_sync_thread;
+ SDL_sem *m_sync_sem;
+ CMsgQueue *m_master_msg_queue;
+ SDL_sem *m_master_msg_queue_sem;
+ CMsgQueue m_sync_thread_msg_queue;
+ range_desc_t *m_range;
+ int m_session_control_is_aggregate;
+ int m_waiting_for_audio;
+ int m_audio_volume;
+ int m_screen_scale;
+ int m_fullscreen;
+ int m_pixel_height;
+ int m_pixel_width;
+ int m_seekable;
+ volatile int m_sync_pause_done;
+ session_state_t m_session_state;
+ int m_hardware_error;
+ #define SESSION_DESC_COUNT 4
+ const char *m_session_desc[SESSION_DESC_COUNT];
+ media_close_callback_f m_media_close_callback;
+ void *m_media_close_callback_data;
+ int m_streaming_media_set_up;
+ CIpPort *m_unused_ports;
+ int m_rtp_over_rtsp;
+ uint64_t m_first_time_played;
+ bool m_have_audio_rtcp_sync;
+ rtcp_sync_t m_audio_rtcp_sync;
+ t_pdp_mp4player *m_father;
+};
+
+int pdp_sync_thread(void *data);
+
+#endif
diff --git a/include/pdp_mp4player~.h b/include/pdp_mp4player~.h
new file mode 100644
index 0000000..05e6d14
--- /dev/null
+++ b/include/pdp_mp4player~.h
@@ -0,0 +1,118 @@
+
+#ifndef __PDP_MP4PLAYER__
+#define __PDP_MP4PLAYER__
+
+struct pdp_mp4player_struct;
+typedef struct pdp_mp4player_struct t_pdp_mp4player;
+
+#include "pdp.h"
+#include "yuv.h"
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include "pdp_mp4playersession.h"
+#include "pdp_mp4playermedia.h"
+#include "pdp_mp4videosync.h"
+#include "pdp_mp4audiosync.h"
+#include "media_utils.h"
+#include "codec_plugin_private.h"
+#include "our_config_file.h"
+#include "player_util.h"
+#include <rtp/debug.h>
+#include <libhttp/http.h>
+
+
+/* mpeg4ip includes taken from the source tree ( not exported ) */
+#include <mp4.h>
+#undef DECLARE_CONFIG_VARIABLES
+#include "config_set.h"
+
+#undef CONFIG_BOOL
+#define CONFIG_BOOL(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_BOOL, (defval), (defval) }
+#undef CONFIG_FLOAT
+#define CONFIG_FLOAT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_FLOAT,(float) (defval), (float) (defval) }
+#undef CONFIG_INT
+#define CONFIG_INT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_INTEGER,(config_integer_t) (defval), (config_integer_t)(defval) }
+#undef CONFIG_STRING
+#define CONFIG_STRING(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_STRING, (defval), (defval) }
+
+#include "pdp_mp4config.h"
+
+#undef DECLARE_CONFIG_VARIABLES
+#ifndef debug_message
+#define debug_message post
+#endif
+#include "rtp_transmitter.h"
+#include "pdp_mp4videosource.h"
+#include "pdp_mp4audiosource.h"
+
+#define DEFAULT_CHANNELS 1
+#define MIN_PRIORITY -20
+#define DEFAULT_PRIORITY 0
+#define MAX_PRIORITY 20
+
+#define VIDEO_BUFFER_SIZE (1024*1024)
+#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
+#define MIN_AUDIO_SIZE (64 * 1024)
+#define AUDIO_PACKET_SIZE (2*1152)
+
+typedef struct pdp_mp4player_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_int x_packet0;
+ t_int x_dropped;
+
+ t_pdp *x_header;
+ short int *x_data;
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+
+ t_outlet *x_pdp_out; // output decoded pdp packets
+ t_outlet *x_outlet_left; // left audio output
+ t_outlet *x_outlet_right; // right audio output
+ t_outlet *x_outlet_streaming; // indicates the action of streaming
+ t_outlet *x_outlet_nbframes; // number of frames emitted
+ t_outlet *x_outlet_framerate; // real framerate
+
+ char *x_url;
+ t_int x_rtpovertcp; // flag to bypass certain firewalls (tcp mode)
+ t_int x_streaming; // streaming flag
+ t_int x_nbframes; // number of frames emitted
+ t_int x_framerate; // framerate
+ t_int x_samplerate; // audio sample rate
+ t_int x_audiochannels; // audio channels
+ t_int x_audioon; // enough audio data to start playing
+ struct timeval x_starttime; // streaming starting time
+ t_int x_cursec; // current second
+ t_int x_secondcount; // number of frames received in the current second
+ pthread_t x_decodechild;// stream decoding thread
+ t_int x_priority; // priority of decoding thread
+ t_int x_newpicture; // flag indicating a new picture
+
+ /* audio structures */
+ t_int x_audio; // flag to activate the decoding of audio
+ short x_audio_buf[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for audio from stream*/
+ short x_audio_in[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for resampled PCM audio */
+ t_int x_audioin_position; // writing position for incoming audio
+
+ /* mpeg4hippies structures */
+ CPlayerSession *x_psession;
+ CMsgQueue x_queue;
+ SDL_sem *x_psem;
+ t_int x_decodingstate; // internal decoding state
+
+} t_pdp_mp4player;
+
+#endif
+
diff --git a/include/pdp_mp4rtpbytestream.h b/include/pdp_mp4rtpbytestream.h
new file mode 100644
index 0000000..bc087ea
--- /dev/null
+++ b/include/pdp_mp4rtpbytestream.h
@@ -0,0 +1,213 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ */
+/*
+ * player_rtp_bytestream.h - provides an RTP bytestream for the codecs
+ * to access
+ */
+
+#ifndef __RTP_BYTESTREAM_H__
+#define __RTP_BYTESTREAM_H__ 1
+#include "our_bytestream.h"
+#include "player_util.h"
+#include "rtp/rtp.h"
+#include <SDL.h>
+#include <SDL_thread.h>
+#include <sdp/sdp.h>
+
+class CRtpByteStreamBase : public COurInByteStream
+{
+ public:
+ CRtpByteStreamBase(const char *name,
+ format_list_t *fmt,
+ unsigned int rtp_pt,
+ int ondemand,
+ uint64_t tickpersec,
+ rtp_packet **head,
+ rtp_packet **tail,
+ int rtp_seq_set,
+ uint16_t rtp_base_seq,
+ int rtp_ts_set,
+ uint32_t rtp_base_ts,
+ int rtcp_received,
+ uint32_t ntp_frac,
+ uint32_t ntp_sec,
+ uint32_t rtp_ts);
+
+ ~CRtpByteStreamBase();
+ int eof (void) { return m_eof; };
+ virtual void reset(void) {
+ player_debug_message("rtp bytestream reset");
+ init();
+ m_buffering = 0;
+ m_base_ts_set = 0;
+ m_rtp_base_seq_set = 0;
+
+ };
+ void set_skip_on_advance (uint32_t bytes_to_skip) {
+ m_skip_on_advance_bytes = bytes_to_skip;
+ };
+ double get_max_playtime (void) {
+ if (m_fmt->media->media_range.have_range) {
+ return m_fmt->media->media_range.range_end;
+ } else if (m_fmt->media->parent->session_range.have_range) {
+ return m_fmt->media->parent->session_range.range_end;
+ }
+ return 0.0;
+ };
+
+ // various routines for RTP interface.
+ void set_rtp_base_ts(uint32_t t, uint64_t value = 0) {
+ m_base_ts_set = true;
+ m_base_rtp_ts = t;
+ m_base_ts = value;
+ };
+ void set_rtp_base_seq(uint16_t s) {
+ m_rtp_base_seq_set = true;
+ m_rtp_base_seq = s;
+ };
+ int can_skip_frame (void) { return 1; } ;
+ void set_wallclock_offset (uint64_t wclock, uint32_t rtp_ts);
+ int rtp_ready (void) {
+ return true;
+ };
+ void recv_callback(struct rtp *session, rtp_event *e);
+ virtual void flush_rtp_packets(void);
+ int recv_task(int waiting);
+ uint32_t get_last_rtp_timestamp (void) {return m_rtptime_last; };
+ void remove_packet_rtp_queue(rtp_packet *pak, int free);
+ void pause(void);
+ void set_sync(CPlayerSession *psptr);
+
+ void syncronize(rtcp_sync_t *sync);
+ protected:
+ bool check_seq (uint16_t seq);
+ void set_last_seq(uint16_t seq);
+ void init(void);
+ // Make sure all classes call this to calculate real time.
+ uint64_t rtp_ts_to_msec(uint32_t rtp_ts, uint64_t uts, uint64_t &wrap_offset);
+ rtp_packet *m_head, *m_tail;
+ int m_offset_in_pak;
+ uint32_t m_skip_on_advance_bytes;
+ uint32_t m_ts;
+ uint64_t m_total;
+ bool m_base_ts_set;
+ uint32_t m_base_rtp_ts;
+ uint64_t m_base_ts;
+ bool m_rtp_base_seq_set;
+ uint16_t m_rtp_base_seq;
+ uint64_t m_timescale;
+ int m_stream_ondemand;
+ uint64_t m_wrap_offset;
+ bool m_rtcp_received;
+ uint64_t m_rtcp_ts;
+ uint32_t m_rtcp_rtp_ts;
+ uint64_t m_wallclock_offset_wrap;
+ void calculate_wallclock_offset_from_rtcp(uint32_t ntp_frac,
+ uint32_t ntp_sec,
+ uint32_t rtp_ts);
+ SDL_mutex *m_rtp_packet_mutex;
+ int m_buffering;
+ uint64_t m_rtp_buffer_time;
+ unsigned int m_rtp_pt;
+ virtual int check_rtp_frame_complete_for_payload_type(void);
+ virtual void rtp_done_buffering(void) {};
+ uint32_t m_rtptime_last;
+ int m_recvd_pak;
+ int m_recvd_pak_timeout;
+ uint64_t m_recvd_pak_timeout_time;
+ uint64_t m_last_realtime;
+ format_list_t *m_fmt;
+ int m_eof;
+ int m_rtpinfo_set_from_pak;
+ uint16_t m_next_seq;
+ bool m_have_first_pak_ts;
+ uint64_t m_first_pak_ts;
+ uint32_t m_first_pak_rtp_ts;
+ CPlayerSession *m_psptr;
+ bool m_have_sync_info;
+ rtcp_sync_t m_sync_info;
+};
+
+class CRtpByteStream : public CRtpByteStreamBase
+{
+ public:
+ CRtpByteStream(const char *name,
+ format_list_t *fmt,
+ unsigned int rtp_pt,
+ int ondemand,
+ uint64_t tickpersec,
+ rtp_packet **head,
+ rtp_packet **tail,
+ int rtp_seq_set,
+ uint16_t rtp_base_seq,
+ int rtp_ts_set,
+ uint32_t rtp_base_ts,
+ int rtcp_received,
+ uint32_t ntp_frac,
+ uint32_t ntp_sec,
+ uint32_t rtp_ts);
+ ~CRtpByteStream();
+ uint64_t start_next_frame(uint8_t **buffer, uint32_t *buflen,
+ void **userdata);
+ int skip_next_frame(uint64_t *ts, int *havesync, uint8_t **buffer,
+ uint32_t *buflen, void **userdata = NULL);
+ void used_bytes_for_frame(uint32_t bytes);
+ int have_no_data(void);
+ void flush_rtp_packets(void);
+ void reset(void);
+ protected:
+ uint8_t *m_buffer;
+ uint32_t m_buffer_len;
+ uint32_t m_buffer_len_max;
+ uint32_t m_bytes_used;
+};
+
+class CAudioRtpByteStream : public CRtpByteStream
+{
+ public:
+ CAudioRtpByteStream(unsigned int rtp_pt,
+ format_list_t *fmt,
+ int ondemand,
+ uint64_t tickpersec,
+ rtp_packet **head,
+ rtp_packet **tail,
+ int rtp_seq_set,
+ uint16_t rtp_base_seq,
+ int rtp_ts_set,
+ uint32_t rtp_base_ts,
+ int rtcp_received,
+ uint32_t ntp_frac,
+ uint32_t ntp_sec,
+ uint32_t rtp_ts);
+ ~CAudioRtpByteStream();
+ int have_no_data(void);
+ int check_rtp_frame_complete_for_payload_type(void);
+ uint64_t start_next_frame(uint8_t **buffer, uint32_t *buflen,
+ void **userdata);
+ void reset(void);
+ private:
+ rtp_packet *m_working_pak;
+};
+int add_rtp_packet_to_queue(rtp_packet *pak,
+ rtp_packet **head,
+ rtp_packet **tail,
+ const char *name);
+#endif
diff --git a/include/pdp_mp4videosource.h b/include/pdp_mp4videosource.h
new file mode 100644
index 0000000..7433d33
--- /dev/null
+++ b/include/pdp_mp4videosource.h
@@ -0,0 +1,90 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#ifndef __PDP_MP4VIDEOSOURCE__
+#define __PDP_MP4VIDEOSOURCE__
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <linux/videodev.h>
+
+#include "media_source.h"
+#include "video_encoder.h"
+
+class CPDPVideoSource : public CMediaSource {
+public:
+ CPDPVideoSource() : CMediaSource() {
+ m_videoMap = NULL;
+ m_videoFrameMap = NULL;
+ }
+
+ bool IsDone() {
+ return false;
+ }
+
+ float GetProgress() {
+ return 0.0;
+ }
+
+ void ProcessVideo(u_int8_t *pY, u_int8_t *pU, u_int8_t *pV);
+
+ void DoStart(void);
+
+ void DoStop(void);
+
+protected:
+ int ThreadMain(void);
+
+ bool Init(void);
+
+ int8_t StartTimeStamp(Timestamp &frameTimestamp);
+
+ bool EndTimeStamp(int8_t frameNumber);
+
+ Timestamp CalculateVideoTimestampFromFrames (uint64_t frame) {
+ double duration = frame;
+ duration *= TimestampTicks;
+ duration /= m_videoSrcFrameRate;
+ return m_videoCaptureStartTimestamp + (Timestamp)duration;
+ }
+protected:
+ u_int8_t m_maxPasses;
+
+ struct video_mbuf m_videoMbuf;
+ void* m_videoMap;
+ struct video_mmap* m_videoFrameMap;
+ Timestamp m_videoCaptureStartTimestamp;
+ uint64_t m_videoFrames;
+ Duration m_videoSrcFrameDuration;
+ int8_t m_captureHead;
+ int8_t m_encodeHead;
+ float m_videoSrcFrameRate;
+ uint64_t *m_videoFrameMapFrame;
+ Timestamp *m_videoFrameMapTimestamp;
+ uint64_t m_lastVideoFrameMapFrameLoaded;
+ Timestamp m_lastVideoFrameMapTimestampLoaded;
+ bool m_cacheTimestamp;
+};
+
+#endif /* __PDP_MP4VIDEOSOURCE__ */
diff --git a/include/pdp_mp4videosync.h b/include/pdp_mp4videosync.h
new file mode 100644
index 0000000..a1bac85
--- /dev/null
+++ b/include/pdp_mp4videosync.h
@@ -0,0 +1,90 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ * video aspect ratio by:
+ * Peter Maersk-Moller peter@maersk-moller.net
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * video.h - contains the interface class between the codec and the video
+ * display hardware.
+ */
+
+#ifndef __PDP_MP4VIDEOSYNC__
+#define __PDP_MP4VIDEOSYNC__
+
+#include "video.h"
+#include "pdp_mp4player~.h"
+
+class CPDPVideoSync : public CVideoSync {
+ public:
+ CPDPVideoSync(CPlayerSession *psptr, t_pdp_mp4player *pdp_father);
+ ~CPDPVideoSync(void);
+ int initialize_video(const char *name); // from sync task
+ int is_video_ready(uint64_t &disptime); // from sync task
+ int64_t play_video_at(uint64_t current_time, // from sync task
+ int &have_eof);
+ int get_video_buffer(uint8_t **y,
+ uint8_t **u,
+ uint8_t **v);
+ void filled_video_buffers(uint64_t time);
+ void set_video_frame(const uint8_t *y, // from codec
+ const uint8_t *u,
+ const uint8_t *v,
+ int m_pixelw_y,
+ int m_pixelw_uv,
+ uint64_t time);
+ void config (int w, int h); // from codec
+ void set_wait_sem (SDL_sem *p) { m_decode_sem = p; }; // from set up
+ void flush_decode_buffers(void); // from decoder task in response to stop
+ void flush_sync_buffers(void); // from sync task in response to stop
+ void play_video(void);
+ private:
+ int m_video_bpp;
+ int m_video_scale;
+ int m_fullscreen;
+ unsigned int m_width, m_height;
+ int m_video_initialized;
+ int m_config_set;
+ int m_paused;
+ int m_double_width;
+ volatile int m_have_data;
+ SDL_Surface *m_screen;
+ SDL_Overlay *m_image;
+ SDL_Rect m_dstrect;
+ uint32_t m_fill_index, m_play_index;
+ int m_decode_waiting;
+ volatile int m_buffer_filled[MAX_VIDEO_BUFFERS];
+ uint8_t *m_y_buffer[MAX_VIDEO_BUFFERS];
+ uint8_t *m_u_buffer[MAX_VIDEO_BUFFERS];
+ uint8_t *m_v_buffer[MAX_VIDEO_BUFFERS];
+ uint64_t m_play_this_at[MAX_VIDEO_BUFFERS];
+ int m_dont_fill;
+ int m_pixel_width;
+ int m_pixel_height;
+ int m_max_width;
+ int m_max_height;
+ t_pdp_mp4player *m_father;
+};
+
+CPDPVideoSync *pdp_create_video_sync(CPlayerSession *psptr, t_pdp_mp4player *pdp_father);
+
+#endif
diff --git a/modules/pdp_ieee1394.c b/modules/pdp_ieee1394.c
new file mode 100644
index 0000000..189398f
--- /dev/null
+++ b/modules/pdp_ieee1394.c
@@ -0,0 +1,483 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This object is a ieee1394 video input object for OSX, using QuickTime
+ * Some code is inspired by pix_video from Gem
+ * Written by Yves Degoyon
+ */
+
+#include "pdp_config.h"
+#include "pdp.h"
+#include "pdp_llconv.h"
+#include "pdp_imageproc.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sched.h>
+#include <pthread.h>
+#include <Carbon/Carbon.h>
+#include <Quicktime/QuickTime.h>
+#include <Quicktime/QuickTimeComponents.h>
+
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+
+typedef struct pdp_ieee1394_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+
+ bool x_initialized;
+ bool x_auto_open;
+
+ t_int x_packet;
+ t_pdp* x_header;
+ short int *x_data;
+ unsigned char *x_sdata; // static data to hold the grabbed images
+
+ unsigned int x_width;
+ unsigned int x_height;
+ unsigned int x_size;
+ int x_channel;
+ pthread_t x_thread_id;
+ int x_continue_thread;
+ unsigned int x_framerate;
+ int x_frame_ready;
+ t_int x_quality;
+
+ SeqGrabComponent x_sg;
+ SGChannel x_vc;
+ short x_pixelDepth;
+ Rect x_srcRect;
+ GWorldPtr x_srcGWorld;
+ PixMapHandle x_pixMap;
+ Ptr x_baseAddr;
+ long x_rowBytes;
+
+} t_pdp_ieee1394;
+
+static void pdp_ieee1394_close(t_pdp_ieee1394 *x)
+{
+ void *dummy;
+
+ /* terminate thread if there is one */
+ if(x->x_continue_thread)
+ {
+ x->x_continue_thread = 0;
+ // pthread_join (x->x_thread_id, &dummy);
+ }
+
+ // free sequence grabber
+ // if (x->x_vc)
+ // {
+ // if (SGDisposeChannel(x->x_sg, x->x_vc))
+ // {
+ // post("pdp_ieee1394: unable to dispose video channel");
+ // }
+ // x->x_vc = NULL;
+ // post("pdp_ieee1394: disposed video channel");
+ // }
+ // if (x->x_sg)
+ // {
+ // if (CloseComponent(x->x_sg))
+ // {
+ // post("pdp_ieee1394: unable to free sequence grabber.");
+ // }
+ // x->x_sg = NULL;
+ // post("pdp_ieee1394: freed sequence grabber.");
+ // }
+ // if (x->x_srcGWorld)
+ // {
+ // DisposeGWorld(x->x_srcGWorld);
+ // post("pdp_ieee1394: disposed world.");
+ // x->x_srcGWorld = NULL;
+ // }
+
+}
+
+static void pdp_ieee1394_capture_frame(t_pdp_ieee1394* x)
+{
+ OSErr err;
+
+ err = SGIdle(x->x_sg);
+ if (err != noErr)
+ {
+ post("pdp_ieee1394: SGIdle failed.");
+ x->x_frame_ready = 0;
+ }
+ else
+ {
+ x->x_frame_ready = 1;
+ }
+}
+
+
+static void *pdp_ieee1394_thread(void *voidx)
+{
+ t_pdp_ieee1394 *x = ((t_pdp_ieee1394 *)voidx);
+
+ /* capture with a double buffering scheme */
+ while (true)
+ {
+ if (x->x_continue_thread)
+ {
+ /* schedule capture command for next frame */
+ pdp_ieee1394_capture_frame(x);
+ }
+ else
+ {
+ sleep(1);
+ }
+ }
+
+ x->x_thread_id = 0;
+ return 0;
+}
+
+static void pdp_ieee1394_reset(t_pdp_ieee1394 *x)
+{
+ OSErr anErr;
+
+ if ( !x->x_initialized )
+ {
+ post("pdp_ieee1394: trying to reset but the sequence grabber is not initialized");
+ return;
+ }
+
+ post("pdp_ieee1394: resetting....");
+
+ switch (x->x_quality)
+ {
+ case 0:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayNormal);
+ post("pdp_ieee1394: set sequence grabber to : normal quality");
+ break;
+ case 1:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayHighQuality);
+ post("pdp_ieee1394: set sequence grabber to : high quality");
+ break;
+ case 2:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayFast);
+ post("pdp_ieee1394: set sequence grabber to : fast quality");
+ break;
+ case 3:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayAllData);
+ post("pdp_ieee1394: set sequence grabber to : play all data");
+ break;
+ }
+
+ post("pdp_ieee1394: done.");
+}
+
+static void pdp_ieee1394_quality(t_pdp_ieee1394 *x, t_floatarg fquality)
+{
+ if ( ( (t_int)fquality < 0 ) || ( (t_int)fquality > 3 ) )
+ {
+ post("pdp_ieee1394: wrong quality %d", (t_int)fquality );
+ return;
+ }
+ else
+ {
+ x->x_quality = (t_int)fquality;
+ }
+}
+
+static void pdp_ieee1394_free(t_pdp_ieee1394 *x)
+{
+ pdp_ieee1394_close(x);
+}
+
+static t_int pdp_ieee1394_init_grabber(t_pdp_ieee1394 *x)
+{
+ OSErr anErr;
+ x->x_srcRect.top = 0;
+ x->x_srcRect.left = 0;
+ x->x_srcRect.bottom = x->x_height;
+ x->x_srcRect.right = x->x_width;
+
+ x->x_sg = OpenDefaultComponent(SeqGrabComponentType, 0);
+ if(x->x_sg==NULL)
+ {
+ post("pdp_ieee1394: could not open default component");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: opened default component");
+ }
+
+ anErr = SGInitialize(x->x_sg);
+ if(anErr!=noErr)
+ {
+ post("pdp_ieee1394: could not initialize sequence grabber");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: initialized sequence grabber");
+ }
+
+ anErr = SGSetDataRef(x->x_sg, 0, 0, seqGrabDontMakeMovie);
+ if (anErr != noErr)
+ {
+ post("pdp_ieee1394: couldn't set data ref");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: set data ref ok.");
+ }
+
+ anErr = SGNewChannel(x->x_sg, VideoMediaType, &x->x_vc);
+ if(anErr!=noErr)
+ {
+ post("pdp_ieee1394: could not create new sequence grabber channnel");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: created new sequence grabber channnel.");
+ }
+
+ anErr = SGSetChannelBounds(x->x_vc, &x->x_srcRect);
+ if(anErr!=noErr)
+ {
+ post("pdp_ieee1394: could not set sequence grabber ChannelBounds ");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: set sequence grabber ChannelBounds");
+ }
+
+ anErr = SGSetChannelUsage(x->x_vc, seqGrabPreview);
+ if(anErr!=noErr)
+ {
+ post("pdp_ieee1394: could not set sequence grabber ChannelUsage ");
+ return -1;
+ }
+ else
+ {
+ post("pdp_ieee1394: set sequence grabber ChannelUsage");
+ }
+
+ switch (x->x_quality)
+ {
+ case 0:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayNormal);
+ post("pdp_ieee1394: set sequence grabber to : normal quality");
+ break;
+ case 1:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayHighQuality);
+ post("pdp_ieee1394: set sequence grabber to : high quality");
+ break;
+ case 2:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayFast);
+ post("pdp_ieee1394: set sequence grabber to : fast quality");
+ break;
+ case 3:
+ anErr = SGSetChannelPlayFlags(x->x_vc, channelPlayAllData);
+ post("pdp_ieee1394: set sequence grabber to : play all data");
+ break;
+ }
+
+ anErr = QTNewGWorldFromPtr (&x->x_srcGWorld,
+ k422YpCbCr8CodecType,
+ &x->x_srcRect,
+ NULL,
+ NULL,
+ 0,
+ x->x_sdata,
+ x->x_width*2);
+ if (anErr!= noErr)
+ {
+ post ("pdp_ieee1394: QTNewGWorldFromPtr returned %d", anErr);
+ return -1;
+ }
+
+ if (NULL == x->x_srcGWorld)
+ {
+ post ("pdp_ieee1394: could not allocate off screen");
+ return -1;
+ }
+ SGSetGWorld(x->x_sg,(CGrafPtr)x->x_srcGWorld, NULL);
+ SGStartPreview(x->x_sg);
+
+ return 0;
+}
+
+static void pdp_ieee1394_open(t_pdp_ieee1394 *x)
+{
+
+ x->x_initialized = true;
+ x->x_continue_thread = 1;
+
+ /* create thread */
+ if ( x->x_thread_id == 0 )
+ {
+ if ( pdp_ieee1394_init_grabber( x ) != 0 )
+ {
+ post("pdp_ieee1394: grabber initialization failed");
+ return;
+ }
+ x->x_frame_ready = 0;
+ pthread_create(&x->x_thread_id, 0, pdp_ieee1394_thread, x);
+ }
+}
+
+static void pdp_ieee1394_bang(t_pdp_ieee1394 *x)
+{
+ unsigned char *pQ;
+ short int *pY, *pU, *pV;
+ t_int px, py;
+
+ if (!(x->x_continue_thread))
+ {
+ post("pdp_ieee1394: not initialized.");
+
+ if (x->x_auto_open)
+ {
+ post("pdp_ieee1394: attempting auto open");
+ pdp_ieee1394_open(x);
+ if (!(x->x_initialized))
+ {
+ post("pdp_ieee1394: auto open failed");
+ return;
+ }
+ }
+ else return;
+ }
+
+ /* do nothing if there is no frame ready */
+ if (!x->x_frame_ready) return;
+
+ x->x_packet = pdp_packet_new_image_YCrCb(x->x_width, x->x_height);
+ x->x_header = pdp_packet_header(x->x_packet);
+
+ if (!x->x_header)
+ {
+ post("pdp_ieee1394: FATAL: can't allocate packet");
+ return;
+ }
+
+ x->x_data = (short int *) pdp_packet_data(x->x_packet);
+ memset( x->x_data, 0x0, (x->x_size+(x->x_size>>1))<<1 );
+ pQ = x->x_sdata;
+ pY = x->x_data;
+ pV = x->x_data+x->x_size;
+ pU = x->x_data+x->x_size+(x->x_size>>2);
+ for ( py=0; py<(t_int)x->x_height; py++ )
+ {
+ for ( px=0; px<(t_int)x->x_width; px++ )
+ {
+ *(pY+py*x->x_width+px) = (*(pQ+1+2*(py*x->x_width+px)))<<7;
+ if ( px%2 == 0 )
+ {
+ *(pU+((py>>1)*(x->x_width>>1)+(px>>1))) = (*(pQ+2*(py*x->x_width+px))-128)<<8;
+ }
+ if ( px%2 == 1 )
+ {
+ *(pV+((py>>1)*(x->x_width>>1)+(px>>1))) = (*(pQ+2*(py*x->x_width+px))-128)<<8;
+ }
+ }
+ }
+
+ pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet);
+
+ x->x_frame_ready = 0;
+}
+
+t_class *pdp_ieee1394_class;
+
+void *pdp_ieee1394_new(t_floatarg fwidth, t_floatarg fheight)
+{
+ t_pdp_ieee1394 *x = (t_pdp_ieee1394 *)pd_new(pdp_ieee1394_class);
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+ x->x_initialized = false;
+
+ x->x_auto_open = true;
+
+ x->x_continue_thread = 0;
+
+ if (fwidth > 0.)
+ {
+ x->x_width = (int)fwidth;
+ }
+ else
+ {
+ x->x_width = DEFAULT_WIDTH;
+ }
+
+ if (fheight > 0.)
+ {
+ x->x_height = (int)fheight;
+ }
+ else
+ {
+ x->x_height = DEFAULT_WIDTH;
+ }
+ x->x_size = x->x_width*x->x_height;
+ x->x_sdata = (unsigned char*) getbytes( (x->x_size+(x->x_size>>1))<<1 );
+ if ( !x->x_sdata )
+ {
+ post ("pdp_ieee1394: FATAL : couldn't allocate static data.");
+ return NULL;
+ }
+
+ x->x_quality = 1;
+ x->x_thread_id = 0;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_ieee1394_setup(void)
+{
+ pdp_ieee1394_class = class_new(gensym("pdp_ieee1394"), (t_newmethod)pdp_ieee1394_new,
+ (t_method)pdp_ieee1394_free, sizeof(t_pdp_ieee1394), 0, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_bang, gensym("bang"), A_NULL);
+ class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_close, gensym("close"), A_NULL);
+ class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_open, gensym("open"), A_NULL);
+ class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_reset, gensym("reset"), A_NULL);
+ class_addmethod(pdp_ieee1394_class, (t_method)pdp_ieee1394_quality, gensym("quality"), A_DEFFLOAT, A_NULL);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_mp4audiosource.cpp b/modules/pdp_mp4audiosource.cpp
new file mode 100644
index 0000000..7ae18f4
--- /dev/null
+++ b/modules/pdp_mp4audiosource.cpp
@@ -0,0 +1,204 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000-2002. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#include "m_pd.h"
+
+#ifndef debug_message
+#define debug_message post
+#endif
+#include "pdp_mp4audiosource.h"
+
+//#define DEBUG_TIMESTAMPS 1
+
+CPDPAudioSource::CPDPAudioSource(CLiveConfig *pConfig) : CMediaSource()
+{
+ SetConfig(pConfig);
+
+ m_pcmFrameBuffer = NULL;
+ m_prevTimestamp = 0;
+ m_timestampOverflowArray = NULL;
+ m_timestampOverflowArrayIndex = 0;
+ m_audioOssMaxBufferSize = 0;
+}
+
+int CPDPAudioSource::ThreadMain(void)
+{
+ // just a stub, we don't use the threaded mode
+ return 0;
+}
+
+void CPDPAudioSource::DoStart()
+{
+ if (m_source) {
+ return;
+ }
+
+ if (!Init()) {
+ return;
+ }
+
+ m_source = true;
+}
+
+void CPDPAudioSource::DoStop()
+{
+ if (!m_source) {
+ return;
+ }
+
+ CMediaSource::DoStopAudio();
+
+ CHECK_AND_FREE(m_timestampOverflowArray);
+ free(m_pcmFrameBuffer);
+ m_pcmFrameBuffer = NULL;
+
+ m_source = false;
+}
+
+bool CPDPAudioSource::Init(void)
+{
+ bool rc = InitAudio(true);
+ if (!rc) {
+ return false;
+ }
+
+ m_channelsConfigured = m_pConfig->GetIntegerValue(CONFIG_AUDIO_CHANNELS);
+
+ debug_message("pdp_mp4live~ : init audio : (m_audioDstChannels=%d m_audioDstSampleRate=%d )",
+ m_audioDstChannels, m_audioDstSampleRate);
+
+ rc = SetAudioSrc(PCMAUDIOFRAME,
+ m_channelsConfigured,
+ m_pConfig->GetIntegerValue(CONFIG_AUDIO_SAMPLE_RATE));
+
+ if (!rc) {
+ return false;
+ }
+
+ debug_message("pdp_mp4live~ : set audio src : (m_audioDstSamplesPerFrame=%d m_audioSrcChannels=%d)",
+ m_audioDstSamplesPerFrame, m_audioSrcChannels);
+
+ // for live capture we can match the source to the destination
+ m_audioSrcSamplesPerFrame = m_audioDstSamplesPerFrame;
+ m_pcmFrameSize =
+ m_audioSrcSamplesPerFrame * m_audioSrcChannels * sizeof(u_int16_t);
+
+ if (m_audioOssMaxBufferSize > 0) {
+ size_t array_size;
+ m_audioOssMaxBufferFrames = m_audioOssMaxBufferSize / m_pcmFrameSize;
+ array_size = m_audioOssMaxBufferFrames * sizeof(*m_timestampOverflowArray);
+ m_timestampOverflowArray = (Timestamp *)malloc(array_size);
+ memset(m_timestampOverflowArray, 0, array_size);
+ }
+
+ m_pcmFrameBuffer = (u_int8_t*)malloc(m_pcmFrameSize);
+ if (!m_pcmFrameBuffer) {
+ goto init_failure;
+ }
+
+ // maximum number of passes in ProcessAudio, approx 1 sec.
+ m_maxPasses = m_audioSrcSampleRate / m_audioSrcSamplesPerFrame;
+
+ debug_message("pdp_mp4live~ : audio source initialization done : ( frame size=%d )", m_pcmFrameSize );
+
+ return true;
+
+ init_failure:
+ debug_message("pdp_mp4live~ : audio initialization failed");
+
+ free(m_pcmFrameBuffer);
+ m_pcmFrameBuffer = NULL;
+
+ return false;
+}
+
+void CPDPAudioSource::ProcessAudio(u_int8_t* pcmBuffer, u_int32_t pcmBufferSize)
+{
+ audio_buf_info info;
+ Timestamp currentTime = GetTimestamp();
+ Timestamp timestamp;
+
+ if ( pcmBufferSize > m_pcmFrameSize )
+ {
+ debug_message( "pdp_mp4live~ : too many audio samples : %d should be %d",
+ pcmBufferSize, m_pcmFrameSize );
+ memcpy( m_pcmFrameBuffer, pcmBuffer, m_pcmFrameSize );
+ }
+ else if ( pcmBufferSize < m_pcmFrameSize )
+ {
+ debug_message( "pdp_mp4live~ : too little audio samples : %d should be %d",
+ pcmBufferSize, m_pcmFrameSize );
+ memcpy( m_pcmFrameBuffer, pcmBuffer, pcmBufferSize );
+ }
+ else
+ {
+ memcpy( m_pcmFrameBuffer, pcmBuffer, pcmBufferSize );
+ }
+
+ if (info.bytes == m_audioOssMaxBufferSize) {
+ // means the audio buffer is full, and not capturing
+ // we want to make the timestamp based on the previous one
+ // When we hit this case, we start using the m_timestampOverflowArray
+ // This will give us a timestamp for when the array is full.
+ //
+ // In other words, if we have a full audio buffer (ie: it's not loading
+ // any more), we start storing the current timestamp into the array.
+ // This will let us "catch up", and have a somewhat accurate timestamp
+ // when we loop around
+ //
+ // wmay - I'm not convinced that this actually works - if the buffer
+ // cleans up, we'll ignore m_timestampOverflowArray
+ if (m_timestampOverflowArray != NULL &&
+ m_timestampOverflowArray[m_timestampOverflowArrayIndex] != 0) {
+ timestamp = m_timestampOverflowArray[m_timestampOverflowArrayIndex];
+ } else {
+ timestamp = m_prevTimestamp + SrcSamplesToTicks(m_audioSrcSamplesPerFrame);
+ }
+
+ if (m_timestampOverflowArray != NULL)
+ m_timestampOverflowArray[m_timestampOverflowArrayIndex] = currentTime;
+
+ debug_message("pdp_mp4live~ : audio buffer full !");
+
+ } else {
+ // buffer is not full - so, we make the timestamp based on the number
+ // of bytes in the buffer that we read.
+ timestamp = currentTime - SrcSamplesToTicks(SrcBytesToSamples(info.bytes));
+ if (m_timestampOverflowArray != NULL)
+ m_timestampOverflowArray[m_timestampOverflowArrayIndex] = 0;
+ }
+
+#ifdef DEBUG_TIMESTAMPS
+ debug_message("pdp_mp4live~ : info.bytes=%d t=%llu timestamp=%llu delta=%llu",
+ info.bytes, currentTime, timestamp, timestamp - m_prevTimestamp);
+#endif
+
+ m_prevTimestamp = timestamp;
+ if (m_timestampOverflowArray != NULL) {
+ m_timestampOverflowArrayIndex = (m_timestampOverflowArrayIndex + 1) %
+ m_audioOssMaxBufferFrames;
+ }
+
+ ProcessAudioFrame(m_pcmFrameBuffer, m_pcmFrameSize, timestamp, false);
+}
diff --git a/modules/pdp_mp4audiosync.cpp b/modules/pdp_mp4audiosync.cpp
new file mode 100644
index 0000000..e364c0f
--- /dev/null
+++ b/modules/pdp_mp4audiosync.cpp
@@ -0,0 +1,505 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ *
+ * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * audio.cpp provides an interface (CPDPAudioSync) between the codec and
+ * the SDL audio APIs.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "pdp_mp4playersession.h"
+#include "pdp_mp4audiosync.h"
+#include "player_util.h"
+#include "our_config_file.h"
+#include "m_pd.h"
+
+#define audio_message(loglevel, fmt...) message(loglevel, "audiosync", fmt)
+
+static void pdp_audio_callback (void *userdata, Uint8 *stream, int len)
+{
+ CPDPAudioSync *a = (CPDPAudioSync *)userdata;
+ a->audio_callback(stream, len);
+}
+
+CPDPAudioSync::CPDPAudioSync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) : CAudioSync(psptr)
+{
+ m_fill_index = m_play_index = 0;
+ for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) {
+ m_buffer_filled[ix] = 0;
+ m_sample_buffer[ix] = NULL;
+ }
+ m_buffer_size = 0;
+ m_config_set = 0;
+ m_audio_initialized = 0;
+ m_audio_paused = 1;
+ m_resync_required = 0;
+ m_dont_fill = 0;
+ m_consec_no_buffers = 0;
+ m_audio_waiting_buffer = 0;
+ m_skipped_buffers = 0;
+ m_didnt_fill_buffers = 0;
+ m_play_time = 0 ;
+ m_buffer_latency = 0;
+ m_first_time = 1;
+ m_first_filled = 1;
+ m_buffer_offset_on = 0;
+ m_buffer_ts = 0;
+ m_load_audio_do_next_resync = 0;
+ m_convert_buffer = NULL;
+ m_father = pdp_father;
+}
+
+CPDPAudioSync::~CPDPAudioSync (void)
+{
+ for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) {
+ if (m_sample_buffer[ix] != NULL)
+ free(m_sample_buffer[ix]);
+ m_sample_buffer[ix] = NULL;
+ }
+ CHECK_AND_FREE(m_convert_buffer);
+ audio_message(LOG_NOTICE,
+ "Audio sync skipped %u buffers",
+ m_skipped_buffers);
+ audio_message(LOG_NOTICE, "didn't fill %u buffers", m_didnt_fill_buffers);
+}
+
+void CPDPAudioSync::set_config (int freq,
+ int channels,
+ int format,
+ uint32_t sample_size)
+{
+ if (m_config_set != 0)
+ return;
+
+ if (format == AUDIO_U8 || format == AUDIO_S8)
+ m_bytes_per_sample = 1;
+ else
+ m_bytes_per_sample = 2;
+
+ if (sample_size == 0) {
+ int temp;
+ temp = freq;
+ while ((temp & 0x1) == 0) temp >>= 1;
+ sample_size = temp;
+ while (sample_size < 1024) sample_size *= 2;
+ while (((sample_size * 1000) % freq) != 0) sample_size *= 2;
+ }
+
+ m_buffer_size = channels * sample_size * m_bytes_per_sample;
+
+ for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) {
+ m_buffer_filled[ix] = 0;
+ m_sample_buffer[ix] = (uint8_t *)malloc(2 * m_buffer_size);
+ }
+ m_freq = freq;
+ m_channels = channels;
+ m_format = format;
+ if (m_format == AUDIO_U8) {
+ m_silence = 0x80;
+ } else {
+ m_silence = 0x00;
+ }
+ m_config_set = 1;
+ m_msec_per_frame = (sample_size * 1000) / m_freq;
+ audio_message(LOG_DEBUG, "buffer size %d msec per frame %d", m_buffer_size, m_msec_per_frame);
+};
+
+uint8_t *CPDPAudioSync::get_audio_buffer (void)
+{
+ int ret;
+ int locked = 0;
+ if (m_dont_fill == 1) {
+ return (NULL);
+ }
+
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ ret = m_buffer_filled[m_fill_index];
+ if (ret == 1) {
+ m_audio_waiting_buffer = 1;
+ m_audio_waiting_buffer = 0;
+ if (m_dont_fill != 0) {
+ return (NULL);
+ }
+ locked = 0;
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ ret = m_buffer_filled[m_fill_index];
+ if (locked)
+ if (ret == 1) {
+ post("pdp_mp4audiosync : no buffer");
+ return (NULL);
+ }
+ }
+ return (m_sample_buffer[m_fill_index]);
+}
+
+void CPDPAudioSync::load_audio_buffer (uint8_t *from,
+ uint32_t bytes,
+ uint64_t ts,
+ int resync)
+{
+ uint8_t *to;
+ uint32_t copied;
+ copied = 0;
+ if (m_buffer_offset_on == 0) {
+ int64_t diff = ts - m_buffer_ts;
+
+ if (m_buffer_ts != 0 && diff > 1) {
+ m_load_audio_do_next_resync = 1;
+ audio_message(LOG_DEBUG, "timeslot doesn't match - %llu %llu",
+ ts, m_buffer_ts);
+ }
+ m_buffer_ts = ts;
+ } else {
+ int64_t check;
+ check = ts - m_loaded_next_ts;
+ if (check > m_msec_per_frame) {
+ audio_message(LOG_DEBUG, "potential resync at ts "U64" should be ts "U64,
+ ts, m_loaded_next_ts);
+ uint32_t left;
+ left = m_buffer_size - m_buffer_offset_on;
+ to = get_audio_buffer();
+ memset(to + m_buffer_offset_on, 0, left);
+ filled_audio_buffer(m_buffer_ts, 0);
+ m_buffer_offset_on = 0;
+ m_load_audio_do_next_resync = 1;
+ m_buffer_ts = ts;
+ }
+ }
+ m_loaded_next_ts = bytes * M_64;
+ m_loaded_next_ts /= m_bytes_per_sample;
+ m_loaded_next_ts /= m_freq;
+ m_loaded_next_ts += ts;
+
+ while ( bytes > 0) {
+ to = get_audio_buffer();
+ if (to == NULL) {
+ return;
+ }
+ int copy;
+ uint32_t left;
+
+ left = m_buffer_size - m_buffer_offset_on;
+ copy = MIN(left, bytes);
+ memcpy(to + m_buffer_offset_on, from, copy);
+ bytes -= copy;
+ copied += copy;
+ from += copy;
+ m_buffer_offset_on += copy;
+ if (m_buffer_offset_on >= m_buffer_size) {
+ m_buffer_offset_on = 0;
+ filled_audio_buffer(m_buffer_ts, resync | m_load_audio_do_next_resync);
+ m_buffer_ts += m_msec_per_frame;
+ resync = 0;
+ m_load_audio_do_next_resync = 0;
+ }
+ }
+ return;
+}
+
+void CPDPAudioSync::filled_audio_buffer (uint64_t ts, int resync)
+{
+ uint32_t fill_index;
+ int locked;
+ // m_dont_fill will be set when we have a pause
+ if (m_dont_fill == 1) {
+ return;
+ }
+ // resync = 0;
+ fill_index = m_fill_index;
+ m_fill_index++;
+ m_fill_index %= DECODE_BUFFERS_MAX;
+
+ locked = 0;
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ if (m_first_filled != 0) {
+ m_first_filled = 0;
+ resync = 0;
+ m_resync_required = 0;
+ } else {
+ int64_t diff;
+ diff = ts - m_last_fill_timestamp;
+ if (diff - m_msec_per_frame > m_msec_per_frame) {
+ // have a hole here - don't want to resync
+ if (diff > ((m_msec_per_frame + 1) * 4)) {
+ resync = 1;
+ } else {
+ // try to fill the holes
+ m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra
+ int64_t ts_diff;
+ do {
+ uint8_t *retbuffer;
+ // Get and swap buffers.
+ retbuffer = get_audio_buffer();
+ if (retbuffer == NULL) {
+ return;
+ }
+ if (retbuffer != m_sample_buffer[m_fill_index]) {
+ audio_message(LOG_ERR, "retbuffer not fill index in audio sync");
+ return;
+ }
+ locked = 0;
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ m_sample_buffer[m_fill_index] = m_sample_buffer[fill_index];
+ m_sample_buffer[fill_index] = retbuffer;
+ memset(retbuffer, m_silence, m_buffer_size);
+ m_buffer_time[fill_index] = m_last_fill_timestamp;
+ m_buffer_filled[fill_index] = 1;
+ m_samples_loaded += m_buffer_size;
+ fill_index++;
+ fill_index %= DECODE_BUFFERS_MAX;
+ m_fill_index++;
+ m_fill_index %= DECODE_BUFFERS_MAX;
+ audio_message(LOG_NOTICE, "Filling timestamp %llu with silence",
+ m_last_fill_timestamp);
+ m_last_fill_timestamp += m_msec_per_frame + 1; // fill plus extra
+ ts_diff = ts - m_last_fill_timestamp;
+ audio_message(LOG_DEBUG, "diff is %lld", ts_diff);
+ } while (ts_diff > 0);
+ locked = 0;
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ }
+ } else {
+ if (m_last_fill_timestamp == ts) {
+ audio_message(LOG_NOTICE, "Repeat timestamp with audio %llu", ts);
+ return;
+ }
+ }
+ }
+ m_last_fill_timestamp = ts;
+ m_buffer_filled[fill_index] = 1;
+ m_samples_loaded += m_buffer_size;
+ m_buffer_time[fill_index] = ts;
+ if (resync) {
+ m_resync_required = 1;
+ m_resync_buffer = fill_index;
+#ifdef DEBUG_AUDIO_FILL
+ audio_message(LOG_DEBUG, "Resync from filled_audio_buffer");
+#endif
+ }
+
+ // Check this - we might not want to do this unless we're resyncing
+ if (resync) m_psptr->wake_sync_thread();
+#ifdef DEBUG_AUDIO_FILL
+ audio_message(LOG_DEBUG, "Filling " LLU " %u %u", ts, fill_index, m_samples_loaded);
+#endif
+}
+
+void CPDPAudioSync::set_eof(void)
+{
+ uint8_t *to;
+ if (m_buffer_offset_on != 0) {
+ to = get_audio_buffer();
+ if (to != NULL) {
+ uint32_t left;
+ left = m_buffer_size - m_buffer_offset_on;
+ memset(to + m_buffer_offset_on, 0, left);
+ m_buffer_offset_on = 0;
+ filled_audio_buffer(m_buffer_ts, 0);
+ m_buffer_ts += m_msec_per_frame;
+ }
+ }
+ CAudioSync::set_eof();
+}
+
+int CPDPAudioSync::initialize_audio (int have_video)
+{
+ return (1);
+}
+
+int CPDPAudioSync::is_audio_ready (uint64_t &disptime)
+{
+ disptime = m_buffer_time[m_play_index];
+ return (m_dont_fill == 0 && m_buffer_filled[m_play_index] == 1);
+}
+
+uint64_t CPDPAudioSync::check_audio_sync (uint64_t current_time, int &have_eof)
+{
+ return (0);
+}
+
+void CPDPAudioSync::audio_callback (Uint8 *stream, int ilen)
+{
+ int freed_buffer = 0;
+ uint32_t bufferBytes = (uint32_t)ilen;
+ uint64_t this_time;
+ int delay = 0;
+ int playtime;
+
+}
+
+void CPDPAudioSync::play_audio (void)
+{
+ m_first_time = 1;
+ m_audio_paused = 0;
+ m_play_sample_index = 0;
+}
+
+void CPDPAudioSync::flush_sync_buffers (void)
+{
+ clear_eof();
+ m_dont_fill = 1;
+ if (m_audio_waiting_buffer) {
+ m_audio_waiting_buffer = 0;
+ }
+}
+
+void CPDPAudioSync::flush_decode_buffers (void)
+{
+ int locked = 0;
+ if (m_audio_initialized != 0) {
+ locked = 1;
+ }
+ m_dont_fill = 0;
+ m_first_filled = 1;
+ for (int ix = 0; ix < DECODE_BUFFERS_MAX; ix++) {
+ m_buffer_filled[ix] = 0;
+ }
+ m_buffer_offset_on = 0;
+ m_play_index = m_fill_index = 0;
+ m_audio_paused = 1;
+ m_resync_buffer = 0;
+ m_samples_loaded = 0;
+}
+
+void CPDPAudioSync::set_volume (int volume)
+{
+ m_volume = (volume * SDL_MIX_MAXVOLUME)/100;
+}
+
+void CPDPAudioSync::audio_convert_data (void *from, uint32_t samples)
+{
+ if (m_obtained.format == AUDIO_U8 || m_obtained.format == AUDIO_S8) {
+ // bytewise - easy
+ int8_t *src, *dst;
+ src = (int8_t *) from;
+ dst = (int8_t *) m_convert_buffer;
+ if (m_channels == 2) {
+ // we got 1, wanted 2
+ for (uint32_t ix = 0; ix < samples; ix++) {
+ int16_t sum = *src++;
+ sum += *src++;
+ sum /= 2;
+ if (sum < -128) sum = -128;
+ else if (sum > 128) sum = 128;
+ *dst++ = sum & 0xff;
+ }
+ } else {
+ // we got 2, wanted 1
+ for (uint32_t ix = 0; ix < samples; ix++) {
+ *dst++ = *src;
+ *dst++ = *src++;
+ }
+ }
+ } else {
+ int16_t *src, *dst;
+ src = (int16_t *) from;
+ dst = (int16_t *) m_convert_buffer;
+ samples /= 2;
+ if (m_channels == 1) {
+ // 1 channel to 2
+ for (uint32_t ix = 0; ix < samples; ix++) {
+ *dst++ = *src;
+ *dst++ = *src;
+ src++;
+ }
+ } else {
+ // 2 channels to 1
+ for (uint32_t ix = 0; ix < samples; ix++) {
+ int32_t sum = *src++;
+ sum += *src++;
+ sum /= 2;
+ if (sum < -32768) sum = -32768;
+ else if (sum > 32767) sum = 32767;
+ *dst++ = sum & 0xffff;
+ }
+ }
+
+ }
+}
+
+static void pdp_audio_config (void *ifptr, int freq,
+ int chans, int format, uint32_t max_buffer_size)
+{
+ ((CPDPAudioSync *)ifptr)->set_config(freq,
+ chans,
+ format,
+ max_buffer_size);
+}
+
+static uint8_t *pdp_get_audio_buffer (void *ifptr)
+{
+ return ((CPDPAudioSync *)ifptr)->get_audio_buffer();
+}
+
+static void pdp_filled_audio_buffer (void *ifptr,
+ uint64_t ts,
+ int resync_req)
+{
+ ((CPDPAudioSync *)ifptr)->filled_audio_buffer(ts,
+ resync_req);
+}
+
+static void pdp_load_audio_buffer (void *ifptr,
+ uint8_t *from,
+ uint32_t bytes,
+ uint64_t ts,
+ int resync)
+{
+ ((CPDPAudioSync *)ifptr)->load_audio_buffer(from,
+ bytes,
+ ts,
+ resync);
+}
+
+audio_vft_t audio_vft = {
+ message,
+ pdp_audio_config,
+ pdp_get_audio_buffer,
+ pdp_filled_audio_buffer,
+ pdp_load_audio_buffer
+};
+
+audio_vft_t *get_audio_vft (void)
+{
+ return &audio_vft;
+}
+
+CPDPAudioSync *pdp_create_audio_sync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father)
+{
+ return new CPDPAudioSync(psptr, pdp_father);
+}
+
+int do_we_have_audio (void)
+{
+ return 1;
+}
diff --git a/modules/pdp_mp4config.cpp b/modules/pdp_mp4config.cpp
new file mode 100644
index 0000000..3fbcb9f
--- /dev/null
+++ b/modules/pdp_mp4config.cpp
@@ -0,0 +1,136 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#include "pdp_mp4config.h"
+
+CLiveConfig::CLiveConfig(
+ SConfigVariable* variables,
+ config_index_t numVariables,
+ const char* defaultFileName)
+: CConfigSet(variables, numVariables, defaultFileName)
+{
+ m_appAutomatic = false;
+ m_videoEncode = true;
+ m_videoMaxWidth = 768;
+ m_videoMaxHeight = 576;
+ m_videoNeedRgbToYuv = false;
+ m_videoMpeg4ConfigLength = 0;
+ m_videoMpeg4Config = NULL;
+ m_videoMaxVopSize = 128 * 1024;
+ m_audioEncode = true;
+}
+
+CLiveConfig::~CLiveConfig()
+{
+ CHECK_AND_FREE(m_videoMpeg4Config);
+}
+
+// recalculate derived values
+void CLiveConfig::Update()
+{
+ UpdateVideo();
+ UpdateAudio();
+}
+
+void CLiveConfig::UpdateVideo()
+{
+ m_videoEncode = true;
+
+ CalculateVideoFrameSize();
+
+ GenerateMpeg4VideoConfig(this);
+}
+
+void CLiveConfig::UpdateFileHistory(const char* fileName)
+{
+}
+
+void CLiveConfig::CalculateVideoFrameSize()
+{
+ u_int16_t frameHeight;
+ float aspectRatio = GetFloatValue(CONFIG_VIDEO_ASPECT_RATIO);
+
+ // crop video to appropriate aspect ratio modulo 16 pixels
+ if ((aspectRatio - VIDEO_STD_ASPECT_RATIO) < 0.1) {
+ frameHeight = GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT);
+ } else {
+ frameHeight = (u_int16_t)(
+ (float)GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH)
+ / aspectRatio);
+
+ if ((frameHeight % 16) != 0) {
+ frameHeight += 16 - (frameHeight % 16);
+ }
+
+ if (frameHeight > GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT)) {
+ // OPTION might be better to insert black lines
+ // to pad image but for now we crop down
+ frameHeight = GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT);
+ if ((frameHeight % 16) != 0) {
+ frameHeight -= (frameHeight % 16);
+ }
+ }
+ }
+
+ m_videoWidth = GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH);
+ m_videoHeight = frameHeight;
+
+ m_ySize = m_videoWidth * m_videoHeight;
+ m_uvSize = m_ySize / 4;
+ m_yuvSize = (m_ySize * 3) / 2;
+}
+
+void CLiveConfig::UpdateAudio()
+{
+ m_audioEncode = true;
+}
+
+void CLiveConfig::UpdateRecord()
+{
+}
+
+bool CLiveConfig::IsOneSource()
+{
+ return true;
+}
+
+bool CLiveConfig::IsCaptureVideoSource()
+{
+ return false;
+}
+
+bool CLiveConfig::IsCaptureAudioSource()
+{
+ return false;
+}
+
+bool CLiveConfig::IsFileVideoSource()
+{
+ return false;
+}
+
+bool CLiveConfig::IsFileAudioSource()
+{
+ return false;
+}
diff --git a/modules/pdp_mp4live~.cpp b/modules/pdp_mp4live~.cpp
new file mode 100644
index 0000000..dfa55a5
--- /dev/null
+++ b/modules/pdp_mp4live~.cpp
@@ -0,0 +1,638 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr )
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This object is a mpeg4ip streaming object towards a Darwin or Quicktime streaming server
+ * A lot of this object code is inspired by the code from mpeg4ip
+ * Copyright (c) 2000, 2001, 2002 Dave Mackie, Bill May & others
+ * The rest is written by Yves Degoyon ( ydegoyon@free.fr )
+ */
+
+
+#include "pdp.h"
+#include <math.h>
+#include <time.h>
+#include <sys/time.h>
+
+/* mpeg4ip includes taken from the source tree ( not exported ) */
+#include <mp4.h>
+#define DECLARE_CONFIG_VARIABLES
+#include "config_set.h"
+
+#undef CONFIG_BOOL
+#define CONFIG_BOOL(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_BOOL, (defval), (defval) }
+#undef CONFIG_FLOAT
+#define CONFIG_FLOAT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_FLOAT,(float) (defval), (float) (defval) }
+#undef CONFIG_INT
+#define CONFIG_INT(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_INTEGER,(config_integer_t) (defval), (config_integer_t)(defval) }
+#undef CONFIG_STRING
+#define CONFIG_STRING(var, name, defval) \
+ { &(var), (name), CONFIG_TYPE_STRING, (defval), (defval) }
+
+#include "pdp_mp4config.h"
+
+#undef DECLARE_CONFIG_VARIABLES
+#ifndef debug_message
+#define debug_message post
+#endif
+#include "rtp_transmitter.h"
+#include "pdp_mp4videosource.h"
+#include "pdp_mp4audiosource.h"
+
+#define VIDEO_BUFFER_SIZE (1024*1024)
+#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
+#define AUDIO_PACKET_SIZE (2*1024) /* using aac encoding */
+
+static char *pdp_mp4live_version = "pdp_mp4live~: version 0.1, an mpeg4ip video streaming object ( ydegoyon@free.fr )";
+
+typedef struct pdp_mp4live_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_int x_packet0;
+ t_int x_dropped;
+ t_int x_queue_id;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+
+ t_outlet *x_outlet_streaming; // indicates the status of streaming
+ t_outlet *x_outlet_nbframes; // number of frames emitted
+ t_outlet *x_outlet_framerate; // current frame rate
+
+ t_int x_streaming; // streaming flag
+ t_int x_nbframes; // number of frames emitted
+ t_int x_framerate; // framerate
+
+ t_int x_cursec; // current second
+ t_int x_secondcount; // number of frames emitted in the current second
+
+ /* audio structures */
+ short x_audio_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for incoming audio */
+ short x_audio_enc_buf[2*MAX_AUDIO_PACKET_SIZE]; /* buffer for audio to be encoded */
+ uint8_t x_audio_out[4*MAX_AUDIO_PACKET_SIZE]; /* buffer for encoded audio */
+ t_int x_audioin_position; // writing position for incoming audio
+ t_int x_audio_per_frame; // number of audio samples to transmit for each frame
+
+ /* mpeg4ip data */
+ CLiveConfig *x_mp4Config;
+ CRtpTransmitter *x_rtpTransmitter;
+ CPDPVideoSource *x_videosource;
+ CPDPAudioSource *x_audiosource;
+
+} t_pdp_mp4live;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+static void pdp_mp4live_add_sink(t_pdp_mp4live *x, CMediaSink* pSink)
+{
+ if (x->x_videosource)
+ {
+ x->x_videosource->AddSink(pSink);
+ }
+ if (x->x_audiosource)
+ {
+ x->x_audiosource->AddSink(pSink);
+ }
+}
+
+static void pdp_mp4live_remove_sink(t_pdp_mp4live *x, CMediaSink* pSink)
+{
+ if (x->x_videosource)
+ {
+ x->x_videosource->RemoveSink(pSink);
+ }
+ if (x->x_audiosource)
+ {
+ x->x_audiosource->RemoveSink(pSink);
+ }
+}
+
+static void pdp_mp4live_disconnect(t_pdp_mp4live *x)
+{
+ t_int ret, i;
+
+ if (!x->x_streaming)
+ {
+ post("pdp_mp4live~ : disconnect request but not connected ... ignored" );
+ return;
+ }
+
+ if (x->x_audiosource)
+ {
+ x->x_audiosource->DoStop();
+ delete x->x_audiosource;
+ x->x_audiosource = NULL;
+ }
+
+ if (x->x_videosource)
+ {
+ x->x_videosource->DoStop();
+ delete x->x_videosource;
+ x->x_videosource = NULL;
+ }
+
+ if (x->x_rtpTransmitter)
+ {
+ pdp_mp4live_remove_sink(x, x->x_rtpTransmitter);
+ x->x_rtpTransmitter->StopThread();
+ delete x->x_rtpTransmitter;
+ x->x_rtpTransmitter = NULL;
+ }
+
+ x->x_streaming = 0;
+ outlet_float( x->x_outlet_streaming, x->x_streaming );
+ x->x_nbframes = 0;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+ x->x_framerate = 0;
+ outlet_float( x->x_outlet_framerate, x->x_framerate );
+}
+
+static void pdp_mp4live_connect(t_pdp_mp4live *x )
+{
+ t_int ret, i;
+
+ if (x->x_streaming)
+ {
+ post("pdp_mp4live~ : connect request but already connected ... ignored" );
+ return;
+ }
+
+ post("pdp_mp4live~ : creating video source");
+ if ( x->x_videosource == NULL )
+ {
+ x->x_videosource = new CPDPVideoSource();
+ x->x_videosource->SetConfig(x->x_mp4Config);
+ }
+
+ post("pdp_mp4live~ : creating audio source");
+ if ( x->x_audiosource == NULL )
+ {
+ x->x_audiosource = new CPDPAudioSource(x->x_mp4Config);
+ }
+
+ post("pdp_mp4live~ : creating rtp transmitter");
+ x->x_rtpTransmitter = new CRtpTransmitter(x->x_mp4Config);
+ x->x_rtpTransmitter->StartThread();
+
+ post("pdp_mp4live~ : creating audio destination");
+ x->x_rtpTransmitter->CreateAudioRtpDestination(0,
+ x->x_mp4Config->GetStringValue(CONFIG_RTP_AUDIO_DEST_ADDRESS),
+ x->x_mp4Config->GetIntegerValue(CONFIG_RTP_AUDIO_DEST_PORT),
+ 0);
+
+ post("pdp_mp4live~ : creating video destination");
+ x->x_rtpTransmitter->CreateVideoRtpDestination(0,
+ x->x_mp4Config->GetStringValue(CONFIG_RTP_DEST_ADDRESS),
+ x->x_mp4Config->GetIntegerValue(CONFIG_RTP_VIDEO_DEST_PORT),
+ 0);
+
+ post("pdp_mp4live~ : starting rtp");
+ if ( x->x_rtpTransmitter )
+ {
+ pdp_mp4live_add_sink(x, x->x_rtpTransmitter);
+ x->x_rtpTransmitter->Start();
+ }
+
+ if (x->x_videosource)
+ {
+ post("pdp_mp4live~ : starting video source");
+ x->x_videosource->DoStart();
+ post("pdp_mp4live~ : generating key frame");
+ x->x_videosource->GenerateKeyFrame();
+ }
+
+ if (x->x_audiosource)
+ {
+ post("pdp_mp4live~ : starting audio source");
+ x->x_audiosource->DoStart();
+ }
+
+ x->x_streaming = 1;
+ outlet_float( x->x_outlet_streaming, x->x_streaming );
+ x->x_nbframes = 0;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+ x->x_framerate = 0;
+ outlet_float( x->x_outlet_framerate, x->x_framerate );
+
+}
+
+static void pdp_mp4live_ipaddr(t_pdp_mp4live *x, t_symbol *sIpAddr )
+{
+ t_int a, b, c, d;
+
+ if ( !strcmp( sIpAddr->s_name, "" ) )
+ {
+ post("pdp_mp4live~ : wrong ip address" );
+ return;
+ }
+
+ if ( sscanf( sIpAddr->s_name, "%d.%d.%d.%d", &a, &b, &c, &d ) < 4 )
+ {
+ post("pdp_mp4live~ : wrong ip address : %s", sIpAddr->s_name );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting ip address: %s", sIpAddr->s_name );
+ x->x_mp4Config->SetStringValue( CONFIG_RTP_DEST_ADDRESS, sIpAddr->s_name );
+ x->x_mp4Config->SetStringValue( CONFIG_RTP_AUDIO_DEST_ADDRESS, sIpAddr->s_name );
+}
+
+static void pdp_mp4live_aport(t_pdp_mp4live *x, t_floatarg fAudioPort )
+{
+ if ( ( (t_int) fAudioPort <= 0 ) || ( (t_int) fAudioPort > 65535 ) )
+ {
+ post("pdp_mp4live~ : wrong audio port : %d", fAudioPort );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting audio port: %d", (t_int) fAudioPort );
+ x->x_mp4Config->SetIntegerValue( CONFIG_RTP_AUDIO_DEST_PORT, (t_int) fAudioPort );
+
+}
+
+static void pdp_mp4live_vport(t_pdp_mp4live *x, t_floatarg fVideoPort )
+{
+ if ( ( (t_int) fVideoPort <= 0 ) || ( (t_int) fVideoPort > 65535 ) )
+ {
+ post("pdp_mp4live~ : wrong video port : %d", fVideoPort );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting video port: %d", (t_int) fVideoPort );
+ x->x_mp4Config->SetIntegerValue( CONFIG_RTP_VIDEO_DEST_PORT, (t_int) fVideoPort );
+
+}
+
+static void pdp_mp4live_ttl(t_pdp_mp4live *x, t_floatarg fTtl )
+{
+ if ( ( (t_int) fTtl <= 0 ) || ( (t_int) fTtl > 255 ) )
+ {
+ post("pdp_mp4live~ : wrong ttl : %d", fTtl );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting ttl : %d", (t_int) fTtl );
+ x->x_mp4Config->SetIntegerValue( CONFIG_RTP_MCAST_TTL, (t_int) fTtl );
+
+}
+
+static void pdp_mp4live_vwidth(t_pdp_mp4live *x, t_floatarg fWidth )
+{
+ if ( ( (t_int) fWidth <= 0 ) )
+ {
+ post("pdp_mp4live~ : wrong width : %d", fWidth );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting width : %d", (t_int) fWidth );
+ x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_RAW_WIDTH, (t_int) fWidth );
+
+}
+
+static void pdp_mp4live_vheight(t_pdp_mp4live *x, t_floatarg fHeight )
+{
+ if ( ( (t_int) fHeight <= 0 ) )
+ {
+ post("pdp_mp4live~ : wrong height : %d", fHeight );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting height : %d", (t_int) fHeight );
+ x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_RAW_HEIGHT, (t_int) fHeight );
+
+}
+
+static void pdp_mp4live_framerate(t_pdp_mp4live *x, t_floatarg fFrameRate )
+{
+ if ( ( (t_int) fFrameRate <= 0 ) )
+ {
+ post("pdp_mp4live~ : wrong framerate : %d", fFrameRate );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting framerate : %d", (t_int) fFrameRate );
+ x->x_mp4Config->SetFloatValue( CONFIG_VIDEO_FRAME_RATE, (t_float) fFrameRate );
+
+}
+
+static void pdp_mp4live_vbitrate(t_pdp_mp4live *x, t_floatarg fVBitrate )
+{
+ if ( ( (t_int) fVBitrate <= 0 ) )
+ {
+ post("pdp_mp4live~ : wrong video bit rate : %d", fVBitrate );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting video bit rate : %d", (t_int) fVBitrate );
+ x->x_mp4Config->SetIntegerValue( CONFIG_VIDEO_BIT_RATE, (t_int) fVBitrate );
+
+}
+
+static void pdp_mp4live_samplerate(t_pdp_mp4live *x, t_floatarg fSampleRate )
+{
+ if ( ( (t_int) fSampleRate != 44100 ) &&
+ ( (t_int) fSampleRate != 22050 ) &&
+ ( (t_int) fSampleRate != 11025 ) &&
+ ( (t_int) fSampleRate != 8000 )
+ )
+ {
+ post("pdp_mp4live~ : wrong samplerate : %d", fSampleRate );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting samplerate : %d", (t_int) fSampleRate );
+ x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_SAMPLE_RATE, (t_int) fSampleRate );
+
+}
+
+static void pdp_mp4live_abitrate(t_pdp_mp4live *x, t_floatarg fABitrate )
+{
+ if ( ( (t_int) fABitrate <= 0 ) )
+ {
+ post("pdp_mp4live~ : wrong audio bit rate : %d", fABitrate );
+ return;
+ }
+
+ post( "pdp_mp4live~ : setting audio bit rate : %d", (t_int) fABitrate );
+ x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_BIT_RATE_KBPS, (t_int) fABitrate );
+ x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_BIT_RATE, ((t_int) fABitrate)*1000 );
+
+}
+
+static void pdp_mp4live_sdp(t_pdp_mp4live *x, t_symbol *sSdpFile )
+{
+ t_int ret;
+
+ post( "pdp_mp4live~ : setting sdp filename : %s", (char *) sSdpFile->s_name );
+ x->x_mp4Config->SetStringValue( CONFIG_SDP_FILE_NAME, (char *) sSdpFile->s_name );
+
+ post( "pdp_mp4live~ : writing sdp file : %s", (char *) sSdpFile->s_name );
+ if ( ( ret = GenerateSdpFile( x->x_mp4Config ) ) )
+ {
+ post( "pdp_mp4live~ : written sdp file : %s", (char *) sSdpFile->s_name );
+ }
+ else
+ {
+ post( "pdp_mp4live~ : could not write sdp file : %s",
+ (char *) sSdpFile->s_name );
+ }
+}
+
+static void pdp_mp4live_process_yv12(t_pdp_mp4live *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ u_int8_t *data = (uint8_t *)pdp_packet_data(x->x_packet0);
+ u_int8_t *pY, *pU, *pV;
+ struct timeval etime;
+
+ /* allocate all ressources */
+ if ( ((int)header->info.image.width != x->x_vwidth) ||
+ ((int)header->info.image.height != x->x_vheight) )
+ {
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+ }
+
+ if ( x->x_streaming )
+ {
+ pY = data;
+ pU = data+x->x_vsize;
+ pV = data+x->x_vsize+(x->x_vsize>>2);
+
+ x->x_videosource->ProcessVideo( pY, pV, pU );
+
+ /* update frames counter */
+
+ if ( gettimeofday(&etime, NULL) == -1)
+ {
+ post("pdp_ffmpeg~ : could not read time" );
+ }
+ if ( etime.tv_sec != x->x_cursec )
+ {
+ x->x_cursec = etime.tv_sec;
+ x->x_framerate = x->x_secondcount;
+ x->x_secondcount = 0;
+ }
+ x->x_nbframes++;
+ x->x_secondcount++;
+
+ /* send an audio frame */
+ if ( x->x_audioin_position > x->x_audio_per_frame )
+ {
+ x->x_audiosource->ProcessAudio( (u_int8_t*)x->x_audio_buf,
+ (u_int32_t)x->x_audio_per_frame*sizeof(short) );
+
+ /* output resampled raw samples */
+ memcpy( x->x_audio_buf, x->x_audio_buf+x->x_audio_per_frame,
+ x->x_audioin_position-x->x_audio_per_frame );
+ x->x_audioin_position-=x->x_audio_per_frame;
+ }
+ }
+ return;
+}
+
+static void pdp_mp4live_killpacket(t_pdp_mp4live *x)
+{
+ /* delete source packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+}
+
+ /* store audio data in PCM format and stream it */
+static t_int *pdp_mp4live_perform(t_int *w)
+{
+ t_float *in1 = (t_float *)(w[1]); // left audio inlet
+ t_float *in2 = (t_float *)(w[2]); // right audio inlet
+ t_pdp_mp4live *x = (t_pdp_mp4live *)(w[3]);
+ int n = (int)(w[4]); // number of samples
+ t_float fsample;
+ t_int isample, i;
+
+ // just fills the buffer ( a pcm buffer )
+ while (n--)
+ {
+ fsample=*(in1++);
+ if (fsample > 1.0) { fsample = 1.0; }
+ if (fsample < -1.0) { fsample = -1.0; }
+ isample=(short) (32767.0 * fsample);
+ *(x->x_audio_buf+x->x_audioin_position)=isample;
+ x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE);
+ if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 )
+ {
+ // post( "pdp_mp4live~ : reaching end of audio buffer" );
+ }
+ fsample=*(in2++);
+ if (fsample > 1.0) { fsample = 1.0; }
+ if (fsample < -1.0) { fsample = -1.0; }
+ isample=(short) (32767.0 * fsample);
+ *(x->x_audio_buf+x->x_audioin_position)=isample;
+ x->x_audioin_position=(x->x_audioin_position+1)%(2*MAX_AUDIO_PACKET_SIZE);
+ if ( x->x_audioin_position == 2*MAX_AUDIO_PACKET_SIZE-1 )
+ {
+ // post( "pdp_mp4live~ : reaching end of audio buffer" );
+ }
+ }
+
+ return (w+5);
+}
+
+static void pdp_mp4live_dsp(t_pdp_mp4live *x, t_signal **sp)
+{
+ dsp_add(pdp_mp4live_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
+}
+
+static void pdp_mp4live_process(t_pdp_mp4live *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_BITMAP == header->type)){
+
+ /* pdp_mp4live_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding)
+ {
+
+ case PDP_BITMAP_YV12:
+ pdp_queue_add(x, (void*) pdp_mp4live_process_yv12, (void*) pdp_mp4live_killpacket, &x->x_queue_id);
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+ outlet_float( x->x_outlet_framerate, x->x_framerate );
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_mp4live_process */
+ post( "pdp_mp4live~ : hey!! i don't know about that type of image : %d",
+ pdp_packet_header(x->x_packet0)->info.image.encoding );
+ break;
+
+ }
+ }
+
+}
+
+static void pdp_mp4live_input_0(t_pdp_mp4live *x, t_symbol *s, t_floatarg f)
+{
+
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ /* add the process method and callback to the process queue */
+ pdp_mp4live_process(x);
+ }
+
+}
+
+static void pdp_mp4live_free(t_pdp_mp4live *x)
+{
+ int i;
+
+ pdp_queue_finish(x->x_queue_id);
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+t_class *pdp_mp4live_class;
+
+void *pdp_mp4live_new(void)
+{
+ int i;
+
+ t_pdp_mp4live *x = (t_pdp_mp4live *)pd_new(pdp_mp4live_class);
+ inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal"));
+
+ x->x_outlet_streaming = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float);
+
+ x->x_packet0 = -1;
+ x->x_queue_id = -1;
+ x->x_nbframes = 0;
+ x->x_framerate = 0;
+ x->x_secondcount = 0;
+ x->x_audioin_position = 0;
+
+ x->x_mp4Config = new CLiveConfig(PdpConfigVariables,
+ sizeof(PdpConfigVariables) / sizeof(SConfigVariable),
+ "none");
+ if ( x->x_mp4Config == NULL )
+ {
+ post( "pdp_mp4live~ : couldn't allocate default config" );
+ return NULL;
+ }
+
+ x->x_mp4Config->InitializeIndexes();
+
+ x->x_mp4Config->Update();
+
+ // update sample rate with the actual sample rate
+ x->x_mp4Config->SetIntegerValue( CONFIG_AUDIO_SAMPLE_RATE, (t_int) sys_getsr() );
+
+ x->x_videosource = NULL;
+ x->x_audiosource = NULL;
+
+ x->x_audio_per_frame = AUDIO_PACKET_SIZE;
+
+ return (void *)x;
+}
+
+
+void pdp_mp4live_tilde_setup(void)
+{
+ // post( pdp_mp4live_version );
+ pdp_mp4live_class = class_new(gensym("pdp_mp4live~"), (t_newmethod)pdp_mp4live_new,
+ (t_method)pdp_mp4live_free, sizeof(t_pdp_mp4live), 0, A_NULL);
+
+ CLASS_MAINSIGNALIN(pdp_mp4live_class, t_pdp_mp4live, x_f );
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_dsp, gensym("dsp"), A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_connect, gensym("connect"), A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_ipaddr, gensym("ipaddr"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_aport, gensym("audioport"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vport, gensym("videoport"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_ttl, gensym("ttl"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vwidth, gensym("vwidth"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vheight, gensym("vheight"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_framerate, gensym("framerate"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_vbitrate, gensym("vbitrate"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_samplerate, gensym("samplerate"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_abitrate, gensym("abitrate"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_disconnect, gensym("disconnect"), A_NULL);
+ class_addmethod(pdp_mp4live_class, (t_method)pdp_mp4live_sdp, gensym("sdp"), A_SYMBOL, A_NULL);
+ class_sethelpsymbol( pdp_mp4live_class, gensym("pdp_mp4live~.pd") );
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_mp4playermedia.cpp b/modules/pdp_mp4playermedia.cpp
new file mode 100644
index 0000000..75235e7
--- /dev/null
+++ b/modules/pdp_mp4playermedia.cpp
@@ -0,0 +1,2029 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ *
+ * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * player_media.cpp - handle generic information about a stream
+ */
+#include "mpeg4ip.h"
+#include "pdp_mp4playersession.h"
+#include "pdp_mp4playermedia.h"
+#include "pdp_mp4videosync.h"
+#include "pdp_mp4audiosync.h"
+#include "player_sdp.h"
+#include "player_util.h"
+#include <rtp/memory.h>
+#include "pdp_mp4rtpbytestream.h"
+#include "our_config_file.h"
+#include "media_utils.h"
+#include "ip_port.h"
+#include "codec_plugin.h"
+#include "audio.h"
+#include <time.h>
+#include <rtp/memory.h>
+#include "our_config_file.h"
+#include "rtp_plugin.h"
+#include "media_utils.h"
+#include "rfc3119_bytestream.h"
+#include "mpeg3_rtp_bytestream.h"
+#include "codec/mp3/mp3_rtp_bytestream.h"
+#include "rtp_bytestream_plugin.h"
+#include "codec_plugin_private.h"
+
+static int pdp_recv_thread (void *data)
+{
+ CPlayerMedia *media;
+
+ media = (CPlayerMedia *)data;
+ return (media->recv_thread());
+}
+
+static int pdp_decode_thread (void *data)
+{
+ CPlayerMedia *media;
+ media = (CPlayerMedia *)data;
+ return (media->decode_thread());
+}
+
+static void pdp_rtp_packet_callback (void *data,
+ unsigned char interleaved,
+ struct rtp_packet *pak,
+ int len)
+{
+ ((CPlayerMedia *)data)->rtp_receive_packet(interleaved, pak, len);
+}
+
+static int pdp_init_rtp_tcp (void *data)
+{
+ ((CPlayerMedia *)data)->rtp_init_tcp();
+ return 0;
+}
+
+static int pdp_rtp_start (void *data)
+{
+ ((CPlayerMedia *)data)->rtp_start();
+ return 0;
+}
+
+static int pdp_rtp_periodic (void *data)
+{
+ ((CPlayerMedia *)data)->rtp_periodic();
+ return 0;
+}
+
+static void pdp_recv_callback (struct rtp *session, rtp_event *e)
+{
+ CPlayerMedia *m = (CPlayerMedia *)rtp_get_userdata(session);
+ m->recv_callback(session, e);
+}
+
+static int pdp_rtcp_send_packet (void *ud, uint8_t *buffer, int buflen)
+{
+ return ((CPlayerMedia *)ud)->rtcp_send_packet(buffer, buflen);
+}
+
+CPlayerMedia::CPlayerMedia (CPlayerSession *p)
+{
+ m_plugin = NULL;
+ m_plugin_data = NULL;
+ m_next = NULL;
+ m_parent = p;
+ m_media_info = NULL;
+ m_media_fmt = NULL;
+ m_our_port = 0;
+ m_ports = NULL;
+ m_server_port = 0;
+ m_source_addr = NULL;
+ m_recv_thread = NULL;
+ m_rtptime_tickpersec = 0;
+ m_rtsp_base_seq_received = 0;
+ m_rtsp_base_ts_received = 0;
+
+ m_head = NULL;
+ m_rtp_queue_len = 0;
+
+ m_rtp_ssrc_set = FALSE;
+
+ m_rtsp_session = NULL;
+ m_decode_thread_waiting = 0;
+ m_sync_time_set = FALSE;
+ m_decode_thread = NULL;
+ m_decode_thread_sem = NULL;
+ m_video_sync = NULL;
+ m_audio_sync = NULL;
+ m_paused = 0;
+ m_byte_stream = NULL;
+ m_rtp_byte_stream = NULL;
+ m_video_info = NULL;
+ m_audio_info = NULL;
+ m_user_data = NULL;
+ m_rtcp_received = 0;
+ m_streaming = 0;
+ m_stream_ondemand = 0;
+ m_rtp_use_rtsp = 0;
+}
+
+CPlayerMedia::~CPlayerMedia()
+{
+ rtsp_decode_t *rtsp_decode;
+
+ media_message(LOG_DEBUG, "closing down media %d", m_is_video);
+ if (m_rtsp_session) {
+ // If control is aggregate, m_rtsp_session will be freed by
+ // CPDPPlayerSession
+ if (m_parent->session_control_is_aggregate() == 0) {
+ rtsp_send_teardown(m_rtsp_session, NULL, &rtsp_decode);
+ free_decode_response(rtsp_decode);
+ }
+ m_rtsp_session = NULL;
+ }
+
+ if (m_recv_thread) {
+ m_rtp_msg_queue.send_message(MSG_STOP_THREAD);
+ SDL_WaitThread(m_recv_thread, NULL);
+ m_recv_thread = NULL;
+ }
+
+ if (m_decode_thread) {
+ m_decode_msg_queue.send_message(MSG_STOP_THREAD,
+ NULL,
+ 0,
+ m_decode_thread_sem);
+ SDL_WaitThread(m_decode_thread, NULL);
+ m_decode_thread = NULL;
+ }
+
+
+
+ if (m_source_addr != NULL) free(m_source_addr);
+ m_next = NULL;
+ m_parent = NULL;
+
+ if (m_ports) {
+ delete m_ports;
+ m_ports = NULL;
+ }
+ if (m_rtp_byte_stream) {
+ double diff;
+ diff = difftime(time(NULL), m_start_time);
+ media_message(LOG_INFO, "Media %s", m_media_info->media);
+
+ media_message(LOG_INFO, "Time: %g seconds", diff);
+#if 0
+ double div;
+ player_debug_message("Packets received: %u", m_rtp_packet_received);
+ player_debug_message("Payload received: "LLU" bytes", m_rtp_data_received);
+ div = m_rtp_packet_received / diff;
+ player_debug_message("Packets per sec : %g", div);
+ div = UINT64_TO_DOUBLE(m_rtp_data_received);
+ div *= 8.0;
+ div /= diff;
+ media_message(LOG_INFO, "Bits per sec : %g", div);
+#endif
+
+ }
+ if (m_byte_stream) {
+ delete m_byte_stream;
+ m_byte_stream = NULL;
+ m_rtp_byte_stream = NULL;
+ }
+ if (m_video_info) {
+ free(m_video_info);
+ m_video_info = NULL;
+ }
+ if (m_audio_info) {
+ free(m_audio_info);
+ m_audio_info = NULL;
+ }
+ if (m_user_data) {
+ free((void *)m_user_data);
+ m_user_data = NULL;
+ }
+ if (m_decode_thread_sem) {
+ SDL_DestroySemaphore(m_decode_thread_sem);
+ m_decode_thread_sem = NULL;
+ }
+}
+
+void CPlayerMedia::clear_rtp_packets (void)
+{
+ if (m_head != NULL) {
+ m_tail->rtp_next = NULL;
+ while (m_head != NULL) {
+ rtp_packet *p;
+ p = m_head;
+ m_head = m_head->rtp_next;
+ p->rtp_next = p->rtp_prev = NULL;
+ xfree(p);
+ }
+ }
+ m_tail = NULL;
+ m_rtp_queue_len = 0;
+}
+
+int CPlayerMedia::create_common (int is_video, char *errmsg, uint32_t errlen)
+{
+ m_parent->add_media(this);
+ m_is_video = is_video;
+
+ m_decode_thread_sem = SDL_CreateSemaphore(0);
+ m_decode_thread = SDL_CreateThread(pdp_decode_thread, this);
+ if (m_decode_thread_sem == NULL || m_decode_thread == NULL) {
+ const char *outmedia;
+ if (m_media_info == NULL) {
+ outmedia = m_is_video ? "video" : "audio";
+ } else outmedia = m_media_info->media;
+
+ if (errmsg != NULL)
+ snprintf(errmsg, errlen, "Couldn't start media thread for %s",
+ outmedia);
+ media_message(LOG_ERR, "Failed to create decode thread for media %s",
+ outmedia);
+ return (-1);
+ }
+ return 0;
+}
+/*
+ * CPlayerMedia::create - create when we've already got a
+ * bytestream
+ */
+int CPlayerMedia::create (COurInByteStream *b,
+ int is_video,
+ char *errmsg,
+ uint32_t errlen,
+ int streaming)
+{
+ m_byte_stream = b;
+ m_streaming = streaming;
+ return create_common(is_video, errmsg, errlen);
+}
+
+/*
+ * CPlayerMedia::create_streaming - create a streaming media session,
+ * including setting up rtsp session, rtp and rtp bytestream
+ */
+int CPlayerMedia::create_streaming (media_desc_t *sdp_media,
+ char *errmsg,
+ uint32_t errlen,
+ int ondemand,
+ int use_rtsp,
+ int media_number_in_session)
+{
+ char buffer[80];
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+
+ m_streaming = 1;
+ if (sdp_media == NULL) {
+ snprintf(errmsg, errlen, "Internal media error - sdp is NULL");
+ return(-1);
+ }
+
+ if (strncasecmp(sdp_media->proto, "RTP", strlen("RTP")) != 0) {
+ snprintf(errmsg, errlen, "Media %s doesn't use RTP", sdp_media->media);
+ media_message(LOG_ERR, "%s doesn't use RTP", sdp_media->media);
+ return (-1);
+ }
+ if (sdp_media->fmt == NULL) {
+ snprintf(errmsg, errlen, "Media %s doesn't have any usuable formats",
+ sdp_media->media);
+ media_message(LOG_ERR, "%s doesn't have any formats",
+ sdp_media->media);
+ return (-1);
+ }
+
+ m_media_info = sdp_media;
+ m_stream_ondemand = ondemand;
+ if (ondemand != 0) {
+ /*
+ * Get 2 consecutive IP ports. If we don't have this, don't even
+ * bother
+ */
+ if (use_rtsp == 0) {
+ m_ports = new C2ConsecIpPort(m_parent->get_unused_ip_port_ptr());
+ if (m_ports == NULL || !m_ports->valid()) {
+ snprintf(errmsg, errlen, "Could not find any valid IP ports");
+ media_message(LOG_ERR, "Couldn't get valid IP ports");
+ return (-1);
+ }
+ m_our_port = m_ports->first_port();
+
+ /*
+ * Send RTSP setup message - first create the transport string for that
+ * message
+ */
+ create_rtsp_transport_from_sdp(m_parent->get_sdp_info(),
+ m_media_info,
+ m_our_port,
+ buffer,
+ sizeof(buffer));
+ } else {
+ m_rtp_use_rtsp = 1;
+ m_rtp_media_number_in_session = media_number_in_session;
+ snprintf(buffer, sizeof(buffer), "RTP/AVP/TCP;unicast;interleaved=%d-%d",
+ media_number_in_session * 2, (media_number_in_session * 2) + 1);
+ }
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+ cmd.transport = buffer;
+ int err =
+ rtsp_send_setup(m_parent->get_rtsp_client(),
+ m_media_info->control_string,
+ &cmd,
+ &m_rtsp_session,
+ &decode,
+ m_parent->session_control_is_aggregate());
+ if (err != 0) {
+ snprintf(errmsg, errlen, "Couldn't set up session %s",
+ m_media_info->control_string);
+ media_message(LOG_ERR, "Can't create session %s - error code %d",
+ m_media_info->media, err);
+ if (decode != NULL)
+ free_decode_response(decode);
+ return (-1);
+ }
+ cmd.transport = NULL;
+ media_message(LOG_INFO, "Transport returned is %s", decode->transport);
+
+ /*
+ * process the transport they sent. They need to send port numbers,
+ * addresses, rtptime information, that sort of thing
+ */
+ if (m_source_addr == NULL) {
+ m_source_addr = rtsp_get_server_ip_address_string(m_rtsp_session);
+ media_message(LOG_INFO, "setting default source address from rtsp %s", m_source_addr);
+ }
+
+ if (process_rtsp_transport(decode->transport) != 0) {
+ snprintf(errmsg, errlen, "Couldn't process transport information in RTSP response: %s", decode->transport);
+ free_decode_response(decode);
+ return(-1);
+ }
+ free_decode_response(decode);
+ } else {
+ m_server_port = m_our_port = m_media_info->port;
+ }
+ connect_desc_t *cptr;
+ cptr = get_connect_desc_from_media(m_media_info);
+ if (cptr == NULL) {
+ snprintf(errmsg, errlen, "Server did not return address");
+ return (-1);
+ }
+
+ //
+ // okay - here we want to check that the server port is set up, and
+ // go ahead and init rtp, and the associated task
+ //
+ m_start_time = time(NULL);
+
+ if (create_common(strcmp(sdp_media->media, "video") == 0,
+ errmsg, errlen) < 0) {
+ return -1;
+ }
+
+ if (ondemand == 0 || use_rtsp == 0) {
+ m_rtp_inited = 0;
+ m_recv_thread = SDL_CreateThread(pdp_recv_thread, this);
+ if (m_recv_thread == NULL) {
+ snprintf(errmsg, errlen, "Couldn't create media %s RTP recv thread",
+ m_media_info->media);
+ media_message(LOG_ERR, errmsg);
+ return (-1);
+ }
+ while (m_rtp_inited == 0) {
+ SDL_Delay(10);
+ }
+ if (m_rtp_session == NULL) {
+ snprintf(errmsg, errlen, "Could not start RTP - check debug log");
+ media_message(LOG_ERR, errmsg);
+ return (-1);
+ }
+ } else {
+ int ret;
+ ret = rtsp_thread_set_rtp_callback(m_parent->get_rtsp_client(),
+ pdp_rtp_packet_callback,
+ pdp_rtp_periodic,
+ m_rtp_media_number_in_session,
+ this);
+ if (ret < 0) {
+ snprintf(errmsg, errlen, "Can't setup TCP/RTP callback");
+ return -1;
+ }
+ ret = rtsp_thread_perform_callback(m_parent->get_rtsp_client(),
+ pdp_init_rtp_tcp,
+ this);
+ if (ret < 0) {
+ snprintf(errmsg, errlen, "Can't init RTP in RTSP thread");
+ return -1;
+ }
+ }
+ if (m_rtp_session == NULL) {
+ snprintf(errmsg, errlen, "Couldn't create RTP session for media %s",
+ m_media_info->media);
+ media_message(LOG_ERR, errmsg);
+ return (-1);
+ }
+ return (0);
+}
+
+int CPlayerMedia::create_video_plugin (const codec_plugin_t *p,
+ const char *compressor,
+ int type,
+ int profile,
+ format_list_t *sdp_media,
+ video_info_t *video,
+ const uint8_t *user_data,
+ uint32_t userdata_size)
+{
+ if (m_video_sync == NULL) {
+ m_video_sync = m_parent->set_up_video_sync();
+ }
+ if (m_video_sync == NULL) return -1;
+
+ m_plugin = p;
+ m_video_info = video;
+ m_plugin_data = (p->vc_create)(compressor,
+ type,
+ profile, sdp_media,
+ video,
+ user_data,
+ userdata_size,
+ get_video_vft(),
+ m_video_sync);
+ if (m_plugin_data == NULL)
+ return -1;
+
+ if (user_data != NULL)
+ set_user_data(user_data, userdata_size);
+ return 0;
+}
+
+void CPlayerMedia::set_plugin_data (const codec_plugin_t *p,
+ codec_data_t *d,
+ video_vft_t *v,
+ audio_vft_t *a)
+{
+ m_plugin = p;
+ m_plugin_data = d;
+ if (is_video()) {
+ if (m_video_sync == NULL) {
+ m_video_sync = m_parent->set_up_video_sync();
+ }
+ d->ifptr = m_video_sync;
+ d->v.video_vft = v;
+ } else {
+ if (m_audio_sync == NULL) {
+ m_audio_sync = m_parent->set_up_audio_sync();
+ }
+ d->ifptr = m_audio_sync;
+ d->v.audio_vft = a;
+ }
+
+}
+
+int CPlayerMedia::get_plugin_status (char *buffer, uint32_t buflen)
+{
+ if (m_plugin == NULL) return -1;
+
+ if (m_plugin->c_print_status == NULL) return -1;
+
+ return ((m_plugin->c_print_status)(m_plugin_data, buffer, buflen));
+}
+
+int CPlayerMedia::create_audio_plugin (const codec_plugin_t *p,
+ const char *compressor,
+ int type,
+ int profile,
+ format_list_t *sdp_media,
+ audio_info_t *audio,
+ const uint8_t *user_data,
+ uint32_t userdata_size)
+{
+ if (m_audio_sync == NULL) {
+ m_audio_sync = m_parent->set_up_audio_sync();
+ }
+ if (m_audio_sync == NULL) return -1;
+
+ m_audio_info = audio;
+ m_plugin = p;
+ m_plugin_data = (p->ac_create)(compressor,
+ type,
+ profile,
+ sdp_media,
+ audio,
+ user_data,
+ userdata_size,
+ get_audio_vft(),
+ m_audio_sync);
+ if (m_plugin_data == NULL) return -1;
+
+ if (user_data != NULL)
+ set_user_data(user_data, userdata_size);
+ return 0;
+}
+
+/*
+ * CPlayerMedia::do_play - get play command
+ */
+int CPlayerMedia::do_play (double start_time_offset,
+ char *errmsg,
+ uint32_t errlen)
+{
+
+ if (m_streaming != 0) {
+ if (m_stream_ondemand != 0) {
+ /*
+ * We're streaming - send the RTSP play command
+ */
+ if (m_parent->session_control_is_aggregate() == 0) {
+ char buffer[80];
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+ range_desc_t *range;
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+
+ // only do range if we're not paused
+ range = get_range_from_media(m_media_info);
+ if (range != NULL) {
+ if (start_time_offset < range->range_start ||
+ start_time_offset > range->range_end)
+ start_time_offset = range->range_start;
+ // need to check for smpte
+ sprintf(buffer, "npt=%g-%g", start_time_offset, range->range_end);
+ cmd.range = buffer;
+ }
+
+ if (rtsp_send_play(m_rtsp_session, &cmd, &decode) != 0) {
+ media_message(LOG_ERR, "RTSP play command failed");
+ if (errmsg != NULL) {
+ snprintf(errmsg, errlen, "RTSP Play Error %s-%s",
+ decode->retcode,
+ decode->retresp != NULL ? decode->retresp : "");
+ }
+ free_decode_response(decode);
+ return (-1);
+ }
+
+ /*
+ * process the return information
+ */
+ int ret = pdp_process_rtsp_rtpinfo(decode->rtp_info, m_parent, this);
+ if (ret < 0) {
+ media_message(LOG_ERR, "rtsp rtpinfo failed");
+ free_decode_response(decode);
+ if (errmsg != NULL) {
+ snprintf(errmsg, errlen, "RTSP aggregate RtpInfo response failure");
+ }
+ return (-1);
+ }
+ free_decode_response(decode);
+ }
+ if (m_source_addr == NULL) {
+ // get the ip address of the server from the rtsp stack
+ m_source_addr = rtsp_get_server_ip_address_string(m_rtsp_session);
+ media_message(LOG_INFO, "Setting source address from rtsp - %s",
+ m_source_addr);
+ }
+ // ASDF - probably need to do some stuff here for no rtpinfo...
+ /*
+ * set the various play times, and send a message to the recv task
+ * that it needs to start
+ */
+ m_play_start_time = start_time_offset;
+ }
+ if (m_byte_stream != NULL) {
+ m_byte_stream->play((uint64_t)(start_time_offset * 1000.0));
+ }
+ m_paused = 0;
+ if (m_rtp_use_rtsp) {
+ rtsp_thread_perform_callback(m_parent->get_rtsp_client(),
+ pdp_rtp_start,
+ this);
+ }
+ } else {
+ /*
+ * File (or other) playback.
+ */
+ if (m_paused == 0 || start_time_offset == 0.0) {
+ m_byte_stream->reset();
+ }
+ m_byte_stream->play((uint64_t)(start_time_offset * 1000.0));
+ m_play_start_time = start_time_offset;
+ m_paused = 0;
+ start_decoding();
+ }
+ return (0);
+}
+
+/*
+ * CPlayerMedia::do_pause - stop what we're doing
+ */
+int CPlayerMedia::do_pause (void)
+{
+
+ if (m_streaming != 0) {
+ if (m_stream_ondemand != 0) {
+ /*
+ * streaming - send RTSP pause
+ */
+ if (m_parent->session_control_is_aggregate() == 0) {
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+
+ if (rtsp_send_pause(m_rtsp_session, &cmd, &decode) != 0) {
+ media_message(LOG_ERR, "RTSP play command failed");
+ free_decode_response(decode);
+ return (-1);
+ }
+ free_decode_response(decode);
+ }
+ }
+ if (m_recv_thread != NULL) {
+ m_rtp_msg_queue.send_message(MSG_PAUSE_SESSION);
+ }
+ }
+
+ if (m_byte_stream != NULL)
+ m_byte_stream->pause();
+ /*
+ * Pause the various threads
+ */
+ m_decode_msg_queue.send_message(MSG_PAUSE_SESSION,
+ NULL,
+ 0,
+ m_decode_thread_sem);
+ m_paused = 1;
+ return (0);
+}
+
+double CPlayerMedia::get_max_playtime (void)
+{
+ if (m_byte_stream) {
+ return (m_byte_stream->get_max_playtime());
+ }
+ return (0.0);
+}
+
+/***************************************************************************
+ * Transport and RTP-Info RTSP header line parsing.
+ ***************************************************************************/
+#define ADV_SPACE(a) {while (isspace(*(a)) && (*(a) != '\0'))(a)++;}
+
+#define TTYPE(a,b) {a, sizeof(a), b}
+
+static char *transport_parse_unicast (char *transport, CPlayerMedia *m)
+{
+ ADV_SPACE(transport);
+ if (*transport == '\0') return (transport);
+
+ if (*transport != ';')
+ return (NULL);
+ transport++;
+ ADV_SPACE(transport);
+ return (transport);
+}
+
+static char *transport_parse_multicast (char *transport, CPlayerMedia *m)
+{
+ media_message(LOG_ERR,"Received multicast indication during SETUP");
+ return (NULL);
+}
+
+static char *convert_number (char *transport, uint32_t &value)
+{
+ value = 0;
+ while (isdigit(*transport)) {
+ value *= 10;
+ value += *transport - '0';
+ transport++;
+ }
+ return (transport);
+}
+
+static char *convert_hex (char *transport, uint32_t &value)
+{
+ value = 0;
+ while (isxdigit(*transport)) {
+ value *= 16;
+ if (isdigit(*transport))
+ value += *transport - '0';
+ else
+ value += tolower(*transport) - 'a' + 10;
+ transport++;
+ }
+ return (transport);
+}
+
+static char *transport_parse_client_port (char *transport, CPlayerMedia *m)
+{
+ uint32_t port;
+ uint16_t our_port, our_port_max;
+ if (*transport++ != '=') {
+ return (NULL);
+ }
+ ADV_SPACE(transport);
+ transport = convert_number(transport, port);
+ ADV_SPACE(transport);
+ our_port = m->get_our_port();
+ our_port_max = our_port + 1;
+
+ if (port != our_port) {
+ media_message(LOG_ERR, "Returned client port %u doesn't match sent %u",
+ port, our_port);
+ return (NULL);
+ }
+ if (*transport == ';') {
+ transport++;
+ return (transport);
+ }
+ if (*transport == '\0') {
+ return (transport);
+ }
+ if (*transport != '-') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ transport = convert_number(transport, port);
+ if ((port < our_port) ||
+ (port > our_port_max)) {
+ media_message(LOG_ERR, "Illegal client to port %u, range %u to %u",
+ port, our_port, our_port_max);
+ return (NULL);
+ }
+ ADV_SPACE(transport);
+ if (*transport == ';') {
+ transport++;
+ }
+ return(transport);
+}
+
+static char *transport_parse_server_port (char *transport, CPlayerMedia *m)
+{
+ uint32_t fromport, toport;
+
+ if (*transport++ != '=') {
+ return (NULL);
+ }
+ ADV_SPACE(transport);
+ transport = convert_number(transport, fromport);
+ ADV_SPACE(transport);
+
+ m->set_server_port((uint16_t)fromport);
+
+ if (*transport == ';') {
+ transport++;
+ return (transport);
+ }
+ if (*transport == '\0') {
+ return (transport);
+ }
+ if (*transport != '-') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ transport = convert_number(transport, toport);
+ if (toport < fromport || toport > fromport + 1) {
+ media_message(LOG_ERR, "Illegal server to port %u, from is %u",
+ toport, fromport);
+ return (NULL);
+ }
+ ADV_SPACE(transport);
+ if (*transport == ';') {
+ transport++;
+ }
+ return(transport);
+}
+
+static char *transport_parse_source (char *transport, CPlayerMedia *m)
+{
+ char *ptr, *newone;
+ uint32_t addrlen;
+
+ if (*transport != '=') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ ptr = transport;
+ while (*transport != ';' && *transport != '\0') transport++;
+ addrlen = transport - ptr;
+ if (addrlen == 0) {
+ return (NULL);
+ }
+ newone = (char *)malloc(addrlen + 1);
+ if (newone == NULL) {
+ media_message(LOG_ERR, "Can't alloc memory for transport source");
+ return (NULL);
+ }
+ strncpy(newone, ptr, addrlen);
+ newone[addrlen] = '\0';
+ m->set_source_addr(newone);
+ if (*transport == ';') transport++;
+ return (transport);
+}
+
+static char *transport_parse_ssrc (char *transport, CPlayerMedia *m)
+{
+ uint32_t ssrc;
+ if (*transport != '=') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ transport = convert_hex(transport, ssrc);
+ ADV_SPACE(transport);
+ if (*transport != '\0') {
+ if (*transport != ';') {
+ return (NULL);
+ }
+ transport++;
+ }
+ m->set_rtp_ssrc(ssrc);
+ return (transport);
+}
+
+static char *transport_parse_interleave (char *transport, CPlayerMedia *m)
+{
+ uint32_t chan, chan2;
+ if (*transport != '=') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ transport = convert_number(transport, chan);
+ chan2 = m->get_rtp_media_number() * 2;
+ if (chan != chan2) {
+ media_message(LOG_ERR, "Transport interleave not what was requested %d %d",
+ chan, chan2);
+ return NULL;
+ }
+ ADV_SPACE(transport);
+ if (*transport != '\0') {
+ if (*transport != '-') {
+ return (NULL);
+ }
+ transport++;
+ transport = convert_number(transport, chan2);
+ if (chan + 1 != chan2) {
+ media_message(LOG_ERR, "Error in transport interleaved field");
+ return (NULL);
+ }
+
+ if (*transport == '\0') return (transport);
+ }
+ if (*transport != ';') return (NULL);
+ transport++;
+ return (transport);
+}
+
+static char *rtpinfo_parse_ssrc (char *transport, CPlayerMedia *m, int &end)
+{
+ uint32_t ssrc;
+ if (*transport != '=') {
+ return (NULL);
+ }
+ transport++;
+ ADV_SPACE(transport);
+ transport = convert_hex(transport, ssrc);
+ ADV_SPACE(transport);
+ if (*transport != '\0') {
+ if (*transport == ',') {
+ end = 1;
+ } else if (*transport != ';') {
+ return (NULL);
+ }
+ transport++;
+ }
+ m->set_rtp_ssrc(ssrc);
+ return (transport);
+}
+
+static char *rtpinfo_parse_seq (char *rtpinfo, CPlayerMedia *m, int &endofurl)
+{
+ uint32_t seq;
+ if (*rtpinfo != '=') {
+ return (NULL);
+ }
+ rtpinfo++;
+ ADV_SPACE(rtpinfo);
+ rtpinfo = convert_number(rtpinfo, seq);
+ ADV_SPACE(rtpinfo);
+ if (*rtpinfo != '\0') {
+ if (*rtpinfo == ',') {
+ endofurl = 1;
+ } else if (*rtpinfo != ';') {
+ return (NULL);
+ }
+ rtpinfo++;
+ }
+ m->set_rtp_base_seq(seq);
+ return (rtpinfo);
+}
+
+static char *rtpinfo_parse_rtptime (char *rtpinfo,
+ CPlayerMedia *m,
+ int &endofurl)
+{
+ uint32_t rtptime;
+ int neg = 0;
+ if (*rtpinfo != '=') {
+ return (NULL);
+ }
+ rtpinfo++;
+ ADV_SPACE(rtpinfo);
+ if (*rtpinfo == '-') {
+ neg = 1;
+ rtpinfo++;
+ ADV_SPACE(rtpinfo);
+ }
+ rtpinfo = convert_number(rtpinfo, rtptime);
+ ADV_SPACE(rtpinfo);
+ if (*rtpinfo != '\0') {
+ if (*rtpinfo == ',') {
+ endofurl = 1;
+ } else if (*rtpinfo != ';') {
+ return (NULL);
+ }
+ rtpinfo++;
+ }
+ if (neg != 0) {
+ player_error_message("Warning - negative time returned in rtpinfo");
+ rtptime = 0 - rtptime;
+ }
+ m->set_rtp_base_ts(rtptime);
+ return (rtpinfo);
+}
+struct {
+ const char *name;
+ uint32_t namelen;
+ char *(*routine)(char *transport, CPlayerMedia *);
+} transport_types[] =
+{
+ TTYPE("unicast", transport_parse_unicast),
+ TTYPE("multicast", transport_parse_multicast),
+ TTYPE("client_port", transport_parse_client_port),
+ TTYPE("server_port", transport_parse_server_port),
+ TTYPE("source", transport_parse_source),
+ TTYPE("ssrc", transport_parse_ssrc),
+ TTYPE("interleaved", transport_parse_interleave),
+ {NULL, 0, NULL},
+};
+
+int CPlayerMedia::process_rtsp_transport (char *transport)
+{
+ uint32_t protolen;
+ int ix;
+
+ if (transport == NULL)
+ return (-1);
+
+ protolen = strlen(m_media_info->proto);
+
+ if (strncasecmp(transport, m_media_info->proto, protolen) != 0) {
+ media_message(LOG_ERR, "transport %s doesn't match %s", transport,
+ m_media_info->proto);
+ return (-1);
+ }
+ transport += protolen;
+ if (*transport == '/') {
+ transport++;
+ if (m_rtp_use_rtsp) {
+ if (strncasecmp(transport, "TCP", strlen("TCP")) != 0) {
+ media_message(LOG_ERR, "Transport is not TCP");
+ return (-1);
+ }
+ transport += strlen("TCP");
+ } else {
+ if (strncasecmp(transport, "UDP", strlen("UDP")) != 0) {
+ media_message(LOG_ERR, "Transport is not UDP");
+ return (-1);
+ }
+ transport += strlen("UDP");
+ }
+ }
+ if (*transport != ';') {
+ return (-1);
+ }
+ transport++;
+ do {
+ ADV_SPACE(transport);
+ for (ix = 0; transport_types[ix].name != NULL; ix++) {
+ if (strncasecmp(transport,
+ transport_types[ix].name,
+ transport_types[ix].namelen - 1) == 0) {
+ transport += transport_types[ix].namelen - 1;
+ ADV_SPACE(transport);
+ transport = (transport_types[ix].routine)(transport, this);
+ break;
+ }
+ }
+ if (transport_types[ix].name == NULL) {
+ media_message(LOG_INFO, "Illegal mime type in transport - skipping %s",
+ transport);
+ while (*transport != ';' && *transport != '\0') transport++;
+ if (*transport != '\0') transport++;
+ }
+ } while (transport != NULL && *transport != '\0');
+
+ if (transport == NULL) {
+ return (-1);
+ }
+ return (0);
+}
+
+struct {
+ const char *name;
+ uint32_t namelen;
+ char *(*routine)(char *transport, CPlayerMedia *, int &end_for_url);
+} rtpinfo_types[] =
+{
+ TTYPE("seq", rtpinfo_parse_seq),
+ TTYPE("rtptime", rtpinfo_parse_rtptime),
+ TTYPE("ssrc", rtpinfo_parse_ssrc),
+ {NULL, 0, NULL},
+};
+
+int pdp_process_rtsp_rtpinfo (char *rtpinfo,
+ CPlayerSession *session,
+ CPlayerMedia *media)
+{
+ int ix;
+ CPlayerMedia *newmedia;
+ if (rtpinfo == NULL)
+ return (0);
+
+ do {
+ int no_mimes = 0;
+ ADV_SPACE(rtpinfo);
+ if (strncasecmp(rtpinfo, "url", strlen("url")) != 0) {
+ media_message(LOG_ERR, "Url not found");
+ return (-1);
+ }
+ rtpinfo += strlen("url");
+ ADV_SPACE(rtpinfo);
+ if (*rtpinfo != '=') {
+ media_message(LOG_ERR, "Can't find = after url");
+ return (-1);
+ }
+ rtpinfo++;
+ ADV_SPACE(rtpinfo);
+ char *url = rtpinfo;
+ while (*rtpinfo != '\0' && *rtpinfo != ';' && *rtpinfo != ',') {
+ rtpinfo++;
+ }
+ if (*rtpinfo == '\0') {
+ no_mimes = 1;
+ } else {
+ if (*rtpinfo == ',') {
+ no_mimes = 1;
+ }
+ *rtpinfo++ = '\0';
+ }
+ char *temp = url;
+ newmedia = session->rtsp_url_to_media(url);
+ if (newmedia == NULL) {
+ media_message(LOG_ERR, "Can't find media from %s", url);
+ return -1;
+ } else if (media != NULL && media != newmedia) {
+ media_message(LOG_ERR, "Url in rtpinfo does not match media %s", url);
+ return -1;
+ }
+ if (temp != url)
+ free(url);
+
+ if (no_mimes == 0) {
+ int endofurl = 0;
+ do {
+ ADV_SPACE(rtpinfo);
+ for (ix = 0; rtpinfo_types[ix].name != NULL; ix++) {
+ if (strncasecmp(rtpinfo,
+ rtpinfo_types[ix].name,
+ rtpinfo_types[ix].namelen - 1) == 0) {
+ rtpinfo += rtpinfo_types[ix].namelen - 1;
+ ADV_SPACE(rtpinfo);
+ rtpinfo = (rtpinfo_types[ix].routine)(rtpinfo, newmedia, endofurl);
+ break;
+ }
+ }
+ if (rtpinfo_types[ix].name == NULL) {
+#if 1
+ media_message(LOG_INFO, "Unknown mime-type in RtpInfo - skipping %s",
+ rtpinfo);
+#endif
+ while (*rtpinfo != ';' && *rtpinfo != '\0') rtpinfo++;
+ if (*rtpinfo != '\0') rtpinfo++;
+ }
+ } while (endofurl == 0 && rtpinfo != NULL && *rtpinfo != '\0');
+ }
+ newmedia = NULL;
+ } while (rtpinfo != NULL && *rtpinfo != '\0');
+
+ if (rtpinfo == NULL) {
+ return (-1);
+ }
+
+ return (1);
+}
+
+int CPlayerMedia::rtp_receive_packet (unsigned char interleaved,
+ struct rtp_packet *pak,
+ int len)
+{
+ int ret;
+ if ((interleaved & 1) == 0) {
+ ret = rtp_process_recv_data(m_rtp_session, 0, pak, len);
+ if (ret < 0) {
+ xfree(pak);
+ }
+ } else {
+ uint8_t *pakbuf = (uint8_t *)pak;
+ pakbuf += sizeof(rtp_packet_data);
+
+ rtp_process_ctrl(m_rtp_session, pakbuf, len);
+ xfree(pak);
+ ret = 0;
+ }
+ return ret;
+}
+
+void CPlayerMedia::rtp_periodic (void)
+{
+ rtp_send_ctrl(m_rtp_session,
+ m_rtp_byte_stream != NULL ?
+ m_rtp_byte_stream->get_last_rtp_timestamp() : 0,
+ NULL);
+ rtp_update(m_rtp_session);
+ if (m_rtp_byte_stream != NULL) {
+ int ret = m_rtp_byte_stream->recv_task(m_decode_thread_waiting);
+ if (ret > 0) {
+ if (m_rtp_buffering == 0) {
+ m_rtp_buffering = 1;
+ start_decoding();
+ } else {
+ bytestream_primed();
+ }
+ }
+ return;
+ }
+ if (m_head != NULL) {
+ /*
+ * Make sure that the payload type is the same
+ */
+ if (m_head->rtp_pak_pt == m_tail->rtp_pak_pt) {
+ if (m_rtp_queue_len > 10) { // 10 packets consecutive proto same
+ if (determine_payload_type_from_rtp() == FALSE) {
+ clear_rtp_packets();
+ }
+ }
+ } else {
+ clear_rtp_packets();
+ }
+ }
+}
+
+void CPlayerMedia::rtp_start (void)
+{
+ if (m_rtp_ssrc_set == TRUE) {
+ rtp_set_my_ssrc(m_rtp_session, m_rtp_ssrc);
+ } else {
+ // For now - we'll set up not to wait for RTCP validation
+ // before indicating if rtp library should accept.
+ rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE);
+ rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE);
+ }
+ if (m_rtp_byte_stream != NULL) {
+ //m_rtp_byte_stream->reset(); - gets called when pausing
+ m_rtp_byte_stream->flush_rtp_packets();
+ }
+ m_rtp_buffering = 0;
+}
+
+void CPlayerMedia::rtp_end(void)
+{
+ if (m_rtp_session != NULL) {
+ rtp_send_bye(m_rtp_session);
+ rtp_done(m_rtp_session);
+ }
+ m_rtp_session = NULL;
+}
+
+int CPlayerMedia::rtcp_send_packet (uint8_t *buffer, int buflen)
+{
+ if (config.get_config_value(CONFIG_SEND_RTCP_IN_RTP_OVER_RTSP) != 0) {
+ return rtsp_thread_send_rtcp(m_parent->get_rtsp_client(),
+ m_rtp_media_number_in_session,
+ buffer,
+ buflen);
+ }
+ return buflen;
+}
+
+int CPlayerMedia::recv_thread (void)
+{
+ struct timeval timeout;
+ int retcode;
+ CMsg *newmsg;
+ int recv_thread_stop = 0;
+ connect_desc_t *cptr;
+ cptr = get_connect_desc_from_media(m_media_info);
+
+
+ m_rtp_buffering = 0;
+ if (m_stream_ondemand != 0) {
+ /*
+ * We need to free up the ports that we got before RTP tries to set
+ * them up, so we don't have any re-use conflicts. There is a small
+ * window here that they might get used...
+ */
+ delete m_ports; // free up the port numbers
+ m_ports = NULL;
+ }
+
+ double bw;
+
+ if (find_rtcp_bandwidth_from_media(m_media_info, &bw) < 0) {
+ bw = 5000.0;
+ } else {
+ media_message(LOG_DEBUG, "Using bw from sdp %g", bw);
+ }
+ m_rtp_session = rtp_init(m_source_addr == NULL ?
+ cptr->conn_addr : m_source_addr,
+ m_our_port,
+ m_server_port,
+ cptr == NULL ? 1 : cptr->ttl, // need ttl here
+ bw, // rtcp bandwidth ?
+ pdp_recv_callback,
+ (uint8_t *)this);
+ if (m_rtp_session != NULL) {
+ rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE);
+ rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE);
+ rtp_start();
+ }
+ m_rtp_inited = 1;
+
+ while (recv_thread_stop == 0) {
+ if ((newmsg = m_rtp_msg_queue.get_message()) != NULL) {
+ //player_debug_message("recv thread message %d", newmsg->get_value());
+ switch (newmsg->get_value()) {
+ case MSG_STOP_THREAD:
+ recv_thread_stop = 1;
+ break;
+ case MSG_PAUSE_SESSION:
+ // Indicate that we need to restart the session.
+ // But keep going...
+ rtp_start();
+ break;
+ }
+ delete newmsg;
+ newmsg = NULL;
+ }
+ if (recv_thread_stop == 1) {
+ continue;
+ }
+ if (m_rtp_session == NULL) {
+ SDL_Delay(50);
+ } else {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000;
+ retcode = rtp_recv(m_rtp_session, &timeout, 0);
+ // player_debug_message("rtp_recv return %d", retcode);
+ // Run rtp periodic after each packet received or idle time.
+ if (m_paused == 0 || m_stream_ondemand != 0)
+ rtp_periodic();
+ }
+
+ }
+ /*
+ * When we're done, send a bye, close up rtp, and go home
+ */
+ rtp_end();
+ return (0);
+}
+
+void CPlayerMedia::recv_callback (struct rtp *session, rtp_event *e)
+{
+ if (e == NULL) return;
+ /*
+ * If we're paused, just dump the packet. Multicast case
+ */
+ if (m_paused != 0) {
+ if (e->type == RX_RTP) {
+ xfree(e->data);
+ return;
+ }
+ }
+#if DROP_PAKS
+ if (e->type == RX_RTP && dropcount >= 50) {
+ xfree((rtp_packet *)e->data);
+ dropcount = 0;
+ return;
+ } else {
+ dropcount++;
+ }
+#endif
+ if (m_rtp_byte_stream != NULL) {
+ m_rtp_byte_stream->recv_callback(session, e);
+ return;
+ }
+ switch (e->type) {
+ case RX_RTP:
+ /* regular rtp packet - add it to the queue */
+ rtp_packet *rpak;
+
+
+ rpak = (rtp_packet *)e->data;
+ if (rpak->rtp_data_len == 0) {
+ xfree(rpak);
+ } else {
+ rpak->pd.rtp_pd_timestamp = get_time_of_day();
+ rpak->pd.rtp_pd_have_timestamp = true;
+ add_rtp_packet_to_queue(rpak, &m_head, &m_tail, m_is_video ? "video" : "audio");
+ m_rtp_queue_len++;
+ }
+ break;
+ case RX_SR:
+ rtcp_sr *srpak;
+ srpak = (rtcp_sr *)e->data;
+
+ m_rtcp_ntp_frac = srpak->ntp_frac;
+ m_rtcp_ntp_sec = srpak->ntp_sec;
+ m_rtcp_rtp_ts = srpak->rtp_ts;
+ m_rtcp_received = 1;
+ break;
+ case RX_APP:
+ free(e->data);
+ break;
+ default:
+#if 0
+ media_message(LOG_DEBUG, "Thread %u - Callback from rtp with %d %p",
+ SDL_ThreadID(),e->type, e->data);
+#endif
+ break;
+ }
+}
+
+int CPlayerMedia::determine_payload_type_from_rtp(void)
+{
+ uint8_t payload_type = m_head->rtp_pak_pt, temp;
+ format_list_t *fmt;
+ uint64_t tickpersec;
+
+ fmt = m_media_info->fmt;
+ while (fmt != NULL) {
+ // rtp payloads are all numeric
+ temp = atoi(fmt->fmt);
+ if (temp == payload_type) {
+ m_media_fmt = fmt;
+ if (fmt->rtpmap != NULL) {
+ tickpersec = fmt->rtpmap->clock_rate;
+ } else {
+ if (payload_type >= 96) {
+ media_message(LOG_ERR, "Media %s, rtp payload type of %u, no rtp map",
+ m_media_info->media, payload_type);
+ return (FALSE);
+ } else {
+ // generic payload type. between 0 and 23 are audio - most
+ // are 8000
+ // all video (above 25) are 90000
+ tickpersec = 90000;
+ // this will handle the >= 0 case as well.
+ if (payload_type <= 23) {
+ tickpersec = 8000;
+ if (payload_type == 6) {
+ tickpersec = 16000;
+ } else if (payload_type == 10 || payload_type == 11) {
+ tickpersec = 44100;
+ } else if (payload_type == 14)
+ tickpersec = 90000;
+ }
+ }
+ }
+
+ create_rtp_byte_stream(payload_type,
+ tickpersec,
+ fmt);
+ m_rtp_byte_stream->play((uint64_t)(m_play_start_time * 1000.0));
+ m_byte_stream = m_rtp_byte_stream;
+ if (!is_video()) {
+ m_rtp_byte_stream->set_sync(m_parent);
+ } else {
+ m_parent->syncronize_rtp_bytestreams(NULL);
+ }
+#if 1
+ media_message(LOG_DEBUG, "media %s - rtp tps %u ntp per rtp ",
+ m_media_info->media,
+ m_rtptime_tickpersec);
+#endif
+
+ return (TRUE);
+ }
+ fmt = fmt->next;
+ }
+ media_message(LOG_ERR, "Payload type %d not in format list for media %s",
+ payload_type, m_is_video ? "video" : "audio");
+ return (FALSE);
+}
+
+/*
+ * set up rtptime
+ */
+void CPlayerMedia::set_rtp_base_ts (uint32_t time)
+{
+ m_rtsp_base_ts_received = 1;
+ m_rtp_base_ts = time;
+ if (m_rtp_byte_stream != NULL) {
+ m_rtp_byte_stream->set_rtp_base_ts(time);
+ }
+}
+
+void CPlayerMedia::set_rtp_base_seq (uint16_t seq)
+{
+ m_rtsp_base_seq_received = 1;
+ m_rtp_base_seq = seq;
+ if (m_rtp_byte_stream != NULL) {
+ m_rtp_byte_stream->set_rtp_base_seq(seq);
+ }
+}
+
+void CPlayerMedia::rtp_init_tcp (void)
+{
+ connect_desc_t *cptr;
+ double bw;
+
+ if (find_rtcp_bandwidth_from_media(m_media_info, &bw) < 0) {
+ bw = 5000.0;
+ }
+ cptr = get_connect_desc_from_media(m_media_info);
+ m_rtp_session = rtp_init_extern_net(m_source_addr == NULL ?
+ cptr->conn_addr : m_source_addr,
+ m_our_port,
+ m_server_port,
+ cptr->ttl,
+ bw, // rtcp bandwidth ?
+ pdp_recv_callback,
+ pdp_rtcp_send_packet,
+ (uint8_t *)this);
+ rtp_set_option(m_rtp_session, RTP_OPT_WEAK_VALIDATION, FALSE);
+ rtp_set_option(m_rtp_session, RTP_OPT_PROMISC, TRUE);
+ m_rtp_inited = 1;
+
+}
+
+void CPlayerMedia::create_rtp_byte_stream (uint8_t rtp_pt,
+ uint64_t tps,
+ format_list_t *fmt)
+{
+ int codec;
+ rtp_check_return_t plugin_ret;
+ rtp_plugin_t *rtp_plugin;
+
+ rtp_plugin = NULL;
+ plugin_ret = check_for_rtp_plugins(fmt, rtp_pt, &rtp_plugin);
+
+ if (plugin_ret != RTP_PLUGIN_NO_MATCH) {
+ switch (plugin_ret) {
+ case RTP_PLUGIN_MATCH:
+ player_debug_message("Starting rtp bytestream %s from plugin",
+ rtp_plugin->name);
+ m_rtp_byte_stream = new CPluginRtpByteStream(rtp_plugin,
+ fmt,
+ rtp_pt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ return;
+ case RTP_PLUGIN_MATCH_USE_VIDEO_DEFAULT:
+ // just fall through...
+ break;
+ case RTP_PLUGIN_MATCH_USE_AUDIO_DEFAULT:
+ m_rtp_byte_stream =
+ new CAudioRtpByteStream(rtp_pt,
+ fmt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ if (m_rtp_byte_stream != NULL) {
+ player_debug_message("Starting generic audio byte stream");
+ return;
+ }
+
+ default:
+ break;
+ }
+ } else {
+ if (is_video() && (rtp_pt == 32)) {
+ codec = VIDEO_MPEG12;
+ m_rtp_byte_stream = new CMpeg3RtpByteStream(rtp_pt,
+ fmt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ if (m_rtp_byte_stream != NULL) {
+ return;
+ }
+ } else {
+ if (rtp_pt == 14) {
+ codec = MPEG4IP_AUDIO_MP3;
+ } else if (rtp_pt <= 23) {
+ codec = MPEG4IP_AUDIO_GENERIC;
+ } else {
+ if (fmt->rtpmap == NULL) return;
+
+ codec = lookup_audio_codec_by_name(fmt->rtpmap->encode_name);
+ if (codec < 0) {
+ codec = MPEG4IP_AUDIO_NONE; // fall through everything to generic
+ }
+ }
+ switch (codec) {
+ case MPEG4IP_AUDIO_MP3: {
+ m_rtp_byte_stream =
+ new CAudioRtpByteStream(rtp_pt, fmt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ if (m_rtp_byte_stream != NULL) {
+ m_rtp_byte_stream->set_skip_on_advance(4);
+ player_debug_message("Starting mp3 2250 audio byte stream");
+ return;
+ }
+ }
+ break;
+ case MPEG4IP_AUDIO_MP3_ROBUST:
+ m_rtp_byte_stream =
+ new CRfc3119RtpByteStream(rtp_pt, fmt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ if (m_rtp_byte_stream != NULL) {
+ player_debug_message("Starting mpa robust byte stream");
+ return;
+ }
+ break;
+ case MPEG4IP_AUDIO_GENERIC:
+ m_rtp_byte_stream =
+ new CAudioRtpByteStream(rtp_pt, fmt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ if (m_rtp_byte_stream != NULL) {
+ player_debug_message("Starting generic audio byte stream");
+ return;
+ }
+ default:
+ break;
+ }
+ }
+ m_rtp_byte_stream = new CRtpByteStream(fmt->media->media,
+ fmt,
+ rtp_pt,
+ m_stream_ondemand,
+ tps,
+ &m_head,
+ &m_tail,
+ m_rtsp_base_seq_received,
+ m_rtp_base_seq,
+ m_rtsp_base_ts_received,
+ m_rtp_base_ts,
+ m_rtcp_received,
+ m_rtcp_ntp_frac,
+ m_rtcp_ntp_sec,
+ m_rtcp_rtp_ts);
+ }
+}
+
+void CPlayerMedia::syncronize_rtp_bytestreams (rtcp_sync_t *sync)
+{
+ if (!is_video()) {
+ player_error_message("Attempt to syncronize audio byte stream");
+ return;
+ }
+ if (m_rtp_byte_stream != NULL)
+ m_rtp_byte_stream->syncronize(sync);
+}
+
+void CPlayerMedia::start_decoding (void)
+{
+ m_decode_msg_queue.send_message(MSG_START_DECODING,
+ NULL,
+ 0,
+ m_decode_thread_sem);
+}
+
+void CPlayerMedia::bytestream_primed (void)
+{
+ if (m_decode_thread_waiting != 0) {
+ m_decode_thread_waiting = 0;
+ SDL_SemPost(m_decode_thread_sem);
+ }
+}
+
+void CPlayerMedia::wait_on_bytestream (void)
+{
+ m_decode_thread_waiting = 1;
+#ifdef DEBUG_DECODE
+ if (m_media_info)
+ media_message(LOG_INFO, "decode thread %s waiting", m_media_info->media);
+ else
+ media_message(LOG_INFO, "decode thread waiting");
+#endif
+ SDL_SemWait(m_decode_thread_sem);
+ m_decode_thread_waiting = 0;
+}
+
+void CPlayerMedia::parse_decode_message (int &thread_stop, int &decoding)
+{
+ CMsg *newmsg;
+
+ if ((newmsg = m_decode_msg_queue.get_message()) != NULL) {
+#ifdef DEBUG_DECODE_MSGS
+ media_message(LOG_DEBUG, "decode thread message %d",newmsg->get_value());
+#endif
+ switch (newmsg->get_value()) {
+ case MSG_STOP_THREAD:
+ thread_stop = 1;
+ break;
+ case MSG_PAUSE_SESSION:
+ decoding = 0;
+ if (m_video_sync != NULL) {
+ m_video_sync->flush_decode_buffers();
+ }
+ if (m_audio_sync != NULL) {
+ m_audio_sync->flush_decode_buffers();
+ }
+ break;
+ case MSG_START_DECODING:
+ if (m_video_sync != NULL) {
+ m_video_sync->flush_decode_buffers();
+ }
+ if (m_audio_sync != NULL) {
+ m_audio_sync->flush_decode_buffers();
+ }
+ decoding = 1;
+ break;
+ }
+ delete newmsg;
+ }
+}
+
+int CPlayerMedia::decode_thread (void)
+{
+ // uint32_t msec_per_frame = 0;
+ int ret = 0;
+ int thread_stop = 0, decoding = 0;
+ uint32_t decode_skipped_frames = 0;
+ uint64_t ourtime;
+ // Tell bytestream we're starting the next frame - they'll give us
+ // the time.
+ uint8_t *frame_buffer;
+ uint32_t frame_len;
+ void *ud = NULL;
+
+ uint32_t frames_decoded;
+ uint64_t bytes_decoded;
+ uint32_t frames_decoded_last_sec;
+ uint64_t bytes_decoded_last_sec;
+ uint64_t current_second;
+ uint32_t total_secs;
+ uint32_t last_div = 0;
+
+ total_secs = 0;
+ frames_decoded = 0;
+ bytes_decoded = 0;
+ frames_decoded_last_sec = 0;
+ bytes_decoded_last_sec = 0;
+ current_second = 0;
+
+ while (thread_stop == 0) {
+ // waiting here for decoding or thread stop
+ ret = SDL_SemWait(m_decode_thread_sem);
+#ifdef DEBUG_DECODE
+ media_message(LOG_DEBUG, "%s Decode thread awake",
+ is_video() ? "video" : "audio");
+#endif
+ parse_decode_message(thread_stop, decoding);
+
+ if (decoding == 1) {
+ // We've been told to start decoding - if we don't have a codec,
+ // create one
+ if (is_video()) {
+ if (m_video_sync == NULL) {
+ m_video_sync = m_parent->set_up_video_sync();
+ }
+ m_video_sync->set_wait_sem(m_decode_thread_sem);
+ } else {
+ if (m_audio_sync == NULL) {
+ m_audio_sync = m_parent->set_up_audio_sync();
+ }
+ m_audio_sync->set_wait_sem(m_decode_thread_sem);
+ }
+ if (m_plugin == NULL) {
+ if (is_video()) {
+ m_plugin = check_for_video_codec(NULL,
+ m_media_fmt,
+ -1,
+ -1,
+ m_user_data,
+ m_user_data_size);
+ if (m_plugin != NULL) {
+ m_plugin_data = (m_plugin->vc_create)(NULL, // must figure from sdp
+ -1,
+ -1,
+ m_media_fmt,
+ m_video_info,
+ m_user_data,
+ m_user_data_size,
+ get_video_vft(),
+ m_video_sync);
+ if (m_plugin_data == NULL) {
+ m_plugin = NULL;
+ } else {
+ media_message(LOG_DEBUG, "Starting %s codec from decode thread",
+ m_plugin->c_name);
+ }
+ }
+ } else {
+ m_plugin = check_for_audio_codec(NULL,
+ m_media_fmt,
+ -1,
+ -1,
+ m_user_data,
+ m_user_data_size);
+ if (m_plugin != NULL) {
+ m_plugin_data = (m_plugin->ac_create)(NULL,
+ -1,
+ -1,
+ m_media_fmt,
+ m_audio_info,
+ m_user_data,
+ m_user_data_size,
+ get_audio_vft(),
+ m_audio_sync);
+ if (m_plugin_data == NULL) {
+ m_plugin = NULL;
+ } else {
+ media_message(LOG_DEBUG, "Starting %s codec from decode thread",
+ m_plugin->c_name);
+ }
+ }
+ }
+ }
+ if (m_plugin != NULL) {
+ m_plugin->c_do_pause(m_plugin_data);
+ } else {
+ while (thread_stop == 0 && decoding) {
+ SDL_Delay(100);
+ if (m_rtp_byte_stream) {
+ m_rtp_byte_stream->flush_rtp_packets();
+ }
+ parse_decode_message(thread_stop, decoding);
+ }
+ }
+ }
+ /*
+ * this is our main decode loop
+ */
+#ifdef DEBUG_DECODE
+ media_message(LOG_DEBUG, "%s Into decode loop",
+ is_video() ? "video" : "audio");
+#endif
+ frames_decoded_last_sec = 0;
+ bytes_decoded_last_sec = 0;
+ current_second = 0;
+ while ((thread_stop == 0) && decoding) {
+ parse_decode_message(thread_stop, decoding);
+ if (thread_stop != 0)
+ continue;
+ if (decoding == 0) {
+ m_plugin->c_do_pause(m_plugin_data);
+ continue;
+ }
+ if (m_byte_stream->eof()) {
+ media_message(LOG_INFO, "%s hit eof", m_is_video ? "video" : "audio");
+ if (m_audio_sync) m_audio_sync->set_eof();
+ if (m_video_sync) m_video_sync->set_eof();
+ decoding = 0;
+ continue;
+ }
+ if (m_byte_stream->have_no_data()) {
+ // Indicate that we're waiting, and wait for a message from RTP
+ // task.
+ wait_on_bytestream();
+ continue;
+ }
+
+ frame_buffer = NULL;
+ ourtime = m_byte_stream->start_next_frame(&frame_buffer,
+ &frame_len,
+ &ud);
+ /*
+ * If we're decoding video, see if we're playing - if so, check
+ * if we've fallen significantly behind the audio
+ */
+ if (is_video() &&
+ (m_parent->get_session_state() == SESSION_PLAYING)) {
+ uint64_t current_time = m_parent->get_playing_time();
+ if (current_time >= ourtime) {
+#if 1
+ media_message(LOG_INFO, "Candidate for skip decode "U64" our "U64,
+ current_time, ourtime);
+#endif
+ // If the bytestream can skip ahead, let's do so
+ if (m_byte_stream->can_skip_frame() != 0) {
+ int ret;
+ int hassync;
+ int count;
+ current_time += 200;
+ count = 0;
+ // Skip up to the current time + 200 msec
+ ud = NULL;
+ do {
+ if (ud != NULL) free(ud);
+ ret = m_byte_stream->skip_next_frame(&ourtime, &hassync,
+ &frame_buffer, &frame_len,
+ &ud);
+ decode_skipped_frames++;
+ } while (ret != 0 &&
+ !m_byte_stream->eof() &&
+ current_time > ourtime);
+ if (m_byte_stream->eof() || ret == 0) continue;
+#if 1
+ media_message(LOG_INFO, "Skipped ahead "U64 " to "U64,
+ current_time - 200, ourtime);
+#endif
+ /*
+ * Ooh - fun - try to match to the next sync value - if not,
+ * 15 frames
+ */
+ do {
+ if (ud != NULL) free(ud);
+ ret = m_byte_stream->skip_next_frame(&ourtime, &hassync,
+ &frame_buffer, &frame_len,
+ &ud);
+ if (hassync < 0) {
+ uint64_t diff = ourtime - current_time;
+ if (diff > (2 * C_64)) {
+ hassync = 1;
+ }
+ }
+ decode_skipped_frames++;
+ count++;
+ } while (ret != 0 &&
+ hassync <= 0 &&
+ count < 30 &&
+ !m_byte_stream->eof());
+ if (m_byte_stream->eof() || ret == 0) continue;
+#ifdef DEBUG_DECODE
+ media_message(LOG_INFO, "Matched ahead - count %d, sync %d time "U64,
+ count, hassync, ourtime);
+#endif
+ }
+ }
+ }
+#ifdef DEBUG_DECODE
+ media_message(LOG_DEBUG, "Decoding %c frame " U64,
+ m_is_video ? 'v' : 'a', ourtime);
+#endif
+ if (frame_buffer != NULL && frame_len != 0) {
+ int sync_frame;
+ ret = m_plugin->c_decode_frame(m_plugin_data,
+ ourtime,
+ m_streaming != 0,
+ &sync_frame,
+ frame_buffer,
+ frame_len,
+ ud);
+#ifdef DEBUG_DECODE
+ media_message(LOG_DEBUG, "Decoding %c frame return %d",
+ m_is_video ? 'v' : 'a', ret);
+#endif
+ if (ret > 0) {
+ frames_decoded++;
+ m_byte_stream->used_bytes_for_frame(ret);
+ bytes_decoded += ret;
+ last_div = ourtime % 1000;
+ if ((ourtime / 1000) > current_second) {
+ if (frames_decoded_last_sec != 0) {
+#if 0
+ media_message(LOG_DEBUG, "%s - Second "U64", frames %d bytes "U64,
+ m_is_video ? "video" : "audio",
+ current_second,
+ frames_decoded_last_sec,
+ bytes_decoded_last_sec);
+#endif
+ }
+ current_second = ourtime / 1000;
+ total_secs++;
+ frames_decoded_last_sec = 1;
+ bytes_decoded_last_sec = ret;
+ } else {
+ frames_decoded_last_sec++;
+ bytes_decoded_last_sec += ret;
+ }
+ } else {
+ m_byte_stream->used_bytes_for_frame(frame_len);
+ }
+
+ }
+ }
+ // calculate frame rate for session
+ }
+ if (m_is_video)
+ media_message(LOG_NOTICE, "Video decoder skipped %u frames",
+ decode_skipped_frames);
+ if (total_secs != 0) {
+ double fps, bps;
+ double secs;
+ secs = last_div;
+ secs /= 1000.0;
+ secs += total_secs;
+
+ fps = frames_decoded;
+ fps /= secs;
+ bps = UINT64_TO_DOUBLE(bytes_decoded);
+ bps *= 8.0 / secs;
+ media_message(LOG_NOTICE, "%s - bytes "U64", seconds %g, fps %g bps "U64,
+ m_is_video ? "video" : "audio",
+ bytes_decoded, secs,
+ fps, bytes_decoded * 8 / total_secs);
+ }
+ if (m_plugin) {
+ m_plugin->c_close(m_plugin_data);
+ m_plugin_data = NULL;
+ }
+ return (0);
+}
diff --git a/modules/pdp_mp4playersession.cpp b/modules/pdp_mp4playersession.cpp
new file mode 100644
index 0000000..3bd9486
--- /dev/null
+++ b/modules/pdp_mp4playersession.cpp
@@ -0,0 +1,1045 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ * video aspect ratio by:
+ * Peter Maersk-Moller peter @maersk-moller.net
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * pdp_mp4playersession.cpp - describes player session class, which is the
+ * main access point for the player
+ */
+
+#include "mpeg4ip.h"
+#include "pdp_mp4playersession.h"
+#include "pdp_mp4playermedia.h"
+#include "pdp_mp4audiosync.h"
+#include "pdp_mp4videosync.h"
+#include "pdp_mp4player~.h"
+#include "player_sdp.h"
+#include "player_util.h"
+#include <stdlib.h>
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "player_util.h"
+#include "m_pd.h"
+
+#define sync_message(loglevel, fmt...) message(loglevel, "avsync", fmt)
+
+enum {
+ SYNC_STATE_INIT = 0,
+ SYNC_STATE_WAIT_SYNC = 1,
+ SYNC_STATE_WAIT_AUDIO = 2,
+ SYNC_STATE_PLAYING = 3,
+ SYNC_STATE_PAUSED = 4,
+ SYNC_STATE_DONE = 5,
+ SYNC_STATE_EXIT = 6,
+};
+
+#ifdef DEBUG_SYNC_STATE
+const char *sync_state[] = {
+ "Init",
+ "Wait Sync",
+ "Wait Audio",
+ "Playing",
+ "Paused",
+ "Done",
+ "Exit"
+};
+#endif
+
+CPlayerSession::CPlayerSession (CMsgQueue *master_mq,
+ SDL_sem *master_sem,
+ const char *name,
+ t_pdp_mp4player *pdp_father)
+{
+ m_sdp_info = NULL;
+ m_my_media = NULL;
+ m_rtsp_client = NULL;
+ m_video_sync = NULL;
+ m_audio_sync = NULL;
+ m_sync_thread = NULL;
+ m_sync_sem = NULL;
+ m_content_base = NULL;
+ m_master_msg_queue = master_mq;
+ m_master_msg_queue_sem = master_sem;
+ m_paused = 0;
+ m_streaming = 0;
+ m_session_name = strdup(name);
+ m_audio_volume = 75;
+ m_current_time = 0;
+ m_seekable = 0;
+ m_session_state = SESSION_PAUSED;
+ m_clock_wrapped = -1;
+ m_hardware_error = 0;
+ m_pixel_height = -1;
+ m_pixel_width = -1;
+ m_session_control_is_aggregate = 0;
+ for (int ix = 0; ix < SESSION_DESC_COUNT; ix++) {
+ m_session_desc[ix] = NULL;
+ }
+ m_media_close_callback = NULL;
+ m_media_close_callback_data = NULL;
+ m_streaming_media_set_up = 0;
+ m_unused_ports = NULL;
+ m_first_time_played = 0;
+ m_latency = 0;
+ m_have_audio_rtcp_sync = false;
+ m_father = pdp_father;
+}
+
+CPlayerSession::~CPlayerSession ()
+{
+ int hadthread = 0;
+#ifndef NEED_SDL_VIDEO_IN_MAIN_THREAD
+ if (m_sync_thread) {
+ send_sync_thread_a_message(MSG_STOP_THREAD);
+ SDL_WaitThread(m_sync_thread, NULL);
+ m_sync_thread = NULL;
+ hadthread = 1;
+ }
+#else
+ send_sync_thread_a_message(MSG_STOP_THREAD);
+ hadthread = 1;
+#endif
+
+
+
+ if (m_streaming_media_set_up != 0 &&
+ session_control_is_aggregate()) {
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+ rtsp_send_aggregate_teardown(m_rtsp_client,
+ m_sdp_info->control_string,
+ &cmd,
+ &decode);
+ free_decode_response(decode);
+ }
+
+
+ if (m_rtsp_client) {
+ free_rtsp_client(m_rtsp_client);
+ m_rtsp_client = NULL;
+ }
+
+ while (m_my_media != NULL) {
+ CPlayerMedia *p;
+ p = m_my_media;
+ m_my_media = p->get_next();
+ delete p;
+ }
+
+ if (m_sdp_info) {
+ sdp_free_session_desc(m_sdp_info);
+ m_sdp_info = NULL;
+ }
+
+ if (m_sync_sem) {
+ SDL_DestroySemaphore(m_sync_sem);
+ m_sync_sem = NULL;
+ }
+
+ if (m_video_sync != NULL) {
+ delete m_video_sync;
+ m_video_sync = NULL;
+ }
+
+ if (m_audio_sync != NULL) {
+ delete m_audio_sync;
+ m_audio_sync = NULL;
+ }
+ if (m_session_name) {
+ free((void *)m_session_name);
+ m_session_name = NULL;
+ }
+ if (m_content_base) {
+ free((void *) m_content_base);
+ m_content_base = NULL;
+ }
+ for (int ix = 0; ix < SESSION_DESC_COUNT; ix++) {
+ if (m_session_desc[ix] != NULL)
+ free((void *)m_session_desc[ix]);
+ m_session_desc[ix] = NULL;
+ }
+
+ if (m_media_close_callback != NULL) {
+ m_media_close_callback(m_media_close_callback_data);
+ }
+
+ while (m_unused_ports != NULL) {
+ CIpPort *first;
+ first = m_unused_ports;
+ m_unused_ports = first->get_next();
+ delete first;
+ }
+ if (hadthread != 0) {
+ SDL_Quit();
+ }
+}
+
+int CPlayerSession::create_streaming_broadcast (session_desc_t *sdp,
+ char *ermsg,
+ uint32_t errlen)
+{
+ session_set_seekable(0);
+ m_sdp_info = sdp;
+ m_streaming = 1;
+ m_rtp_over_rtsp = 0;
+ return (0);
+}
+
+/*
+ * create_streaming - create a session for streaming. Create an
+ * RTSP session with the server, get the SDP information from it.
+ */
+int CPlayerSession::create_streaming_ondemand (const char *url,
+ char *errmsg,
+ uint32_t errlen,
+ int use_tcp)
+{
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+ sdp_decode_info_t *sdpdecode;
+ int dummy;
+ int err;
+
+ // streaming has seek capability (at least on demand)
+ session_set_seekable(1);
+ post("pdp_mp4playersession : creating streaming %s (use_tcp=%d)", url, use_tcp);
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+
+ /*
+ * create RTSP session
+ */
+ if (use_tcp != 0) {
+ m_rtsp_client = rtsp_create_client_for_rtp_tcp(url, &err);
+ } else {
+ m_rtsp_client = rtsp_create_client(url, &err);
+ }
+ if (m_rtsp_client == NULL) {
+ snprintf(errmsg, errlen, "Failed to create RTSP client");
+ player_error_message("Failed to create rtsp client - error %d", err);
+ return (err);
+ }
+ m_rtp_over_rtsp = use_tcp;
+
+ cmd.accept = "application/sdp";
+
+ /*
+ * Send the RTSP describe. This should return SDP information about
+ * the session.
+ */
+ int rtsp_resp;
+
+ rtsp_resp = rtsp_send_describe(m_rtsp_client, &cmd, &decode);
+ if (rtsp_resp != RTSP_RESPONSE_GOOD) {
+ int retval;
+ if (decode != NULL) {
+ retval = (((decode->retcode[0] - '0') * 100) +
+ ((decode->retcode[1] - '0') * 10) +
+ (decode->retcode[2] - '0'));
+ snprintf(errmsg, errlen, "RTSP describe error %d %s", retval,
+ decode->retresp != NULL ? decode->retresp : "");
+ free_decode_response(decode);
+ } else {
+ retval = -1;
+ snprintf(errmsg, errlen, "RTSP return invalid %d", rtsp_resp);
+ }
+ player_error_message("Describe response not good (error=%s)\n", errmsg);
+ return (retval);
+ }
+
+ sdpdecode = set_sdp_decode_from_memory(decode->body);
+ if (sdpdecode == NULL) {
+ snprintf(errmsg, errlen, "Memory failure");
+ player_error_message("Couldn't get sdp decode\n");
+ free_decode_response(decode);
+ return (-1);
+ }
+
+ /*
+ * Decode the SDP information into structures we can use.
+ */
+ err = sdp_decode(sdpdecode, &m_sdp_info, &dummy);
+ free(sdpdecode);
+ if (err != 0) {
+ snprintf(errmsg, errlen, "Couldn't decode session description %s",
+ decode->body);
+ player_error_message("Couldn't decode sdp %s", decode->body);
+ free_decode_response(decode);
+ return (-1);
+ }
+ if (dummy != 1) {
+ snprintf(errmsg, errlen, "Incorrect number of sessions in sdp decode %d",
+ dummy);
+ player_error_message(errmsg);
+ free_decode_response(decode);
+ return (-1);
+ }
+
+ if (m_sdp_info->control_string != NULL) {
+ set_session_control(1);
+ }
+ /*
+ * Make sure we can use the urls in the sdp info
+ */
+ if (decode->content_location != NULL) {
+ // Note - we may have problems if the content location is not absolute.
+ m_content_base = strdup(decode->content_location);
+ } else if (decode->content_base != NULL) {
+ m_content_base = strdup(decode->content_base);
+ } else {
+ int urllen = strlen(url);
+ if (url[urllen] != '/') {
+ char *temp;
+ temp = (char *)malloc(urllen + 2);
+ strcpy(temp, url);
+ strcat(temp, "/");
+ m_content_base = temp;
+ } else {
+ m_content_base = strdup(url);
+ }
+ }
+
+ convert_relative_urls_to_absolute(m_sdp_info,
+ m_content_base);
+
+ free_decode_response(decode);
+ m_streaming = 1;
+ return (0);
+}
+
+CPDPVideoSync * CPlayerSession::set_up_video_sync (void)
+{
+ if (m_video_sync == NULL) {
+ m_video_sync = pdp_create_video_sync(this, m_father);
+ }
+ return m_video_sync;
+}
+
+CPDPAudioSync *CPlayerSession::set_up_audio_sync (void)
+{
+ if (m_audio_sync == NULL) {
+ m_audio_sync = pdp_create_audio_sync(this, m_father);
+ }
+ return m_audio_sync;
+}
+/*
+ * set_up_sync_thread. Creates the sync thread, and a sync class
+ * for each media
+ */
+void CPlayerSession::set_up_sync_thread(void)
+{
+ CPlayerMedia *media;
+
+ media = m_my_media;
+ while (media != NULL) {
+ if (media->is_video()) {
+ media->set_video_sync(set_up_video_sync());
+ } else {
+ media->set_audio_sync(set_up_audio_sync());
+ }
+ media= media->get_next();
+ }
+ m_sync_sem = SDL_CreateSemaphore(0);
+}
+
+/*
+ * play_all_media - get all media to play
+ */
+int CPlayerSession::play_all_media (int start_from_begin,
+ double start_time,
+ char *errmsg,
+ uint32_t errlen)
+{
+ int ret;
+ CPlayerMedia *p;
+ range_desc_t *range;
+
+ if (m_sdp_info && m_sdp_info->session_range.have_range != FALSE) {
+ range = &m_sdp_info->session_range;
+ } else {
+ range = NULL;
+ p = m_my_media;
+ while (range == NULL && p != NULL) {
+ media_desc_t *media;
+ media = p->get_sdp_media_desc();
+ if (media && media->media_range.have_range) {
+ range = &media->media_range;
+ }
+ p = p->get_next();
+ }
+ }
+ p = m_my_media;
+ m_session_state = SESSION_BUFFERING;
+ if (m_paused == 1 && start_time == 0.0 && start_from_begin == FALSE) {
+ /*
+ * we were paused. Continue.
+ */
+ m_play_start_time = m_current_time;
+ start_time = UINT64_TO_DOUBLE(m_current_time);
+ start_time /= 1000.0;
+ player_debug_message("Restarting at " U64 ", %g", m_current_time, start_time);
+ } else {
+ /*
+ * We might have been paused, but we're told to seek
+ */
+ // Indicate what time we're starting at for sync task.
+ m_play_start_time = (uint64_t)(start_time * 1000.0);
+ }
+ m_paused = 0;
+
+ send_sync_thread_a_message(MSG_START_SESSION);
+ // If we're doing aggregate rtsp, send the play command...
+
+ if (session_control_is_aggregate()) {
+ char buffer[80];
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+ if (range != NULL) {
+ uint64_t stime = (uint64_t)(start_time * 1000.0);
+ uint64_t etime = (uint64_t)(range->range_end * 1000.0);
+ sprintf(buffer, "npt="U64"."U64"-"U64"."U64,
+ stime / 1000, stime % 1000, etime / 1000, etime % 1000);
+ cmd.range = buffer;
+ }
+ if (rtsp_send_aggregate_play(m_rtsp_client,
+ m_sdp_info->control_string,
+ &cmd,
+ &decode) != 0) {
+ if (errmsg != NULL) {
+ snprintf(errmsg, errlen, "RTSP Aggregate Play Error %s-%s",
+ decode->retcode,
+ decode->retresp != NULL ? decode->retresp : "");
+ }
+ player_debug_message("RTSP aggregate play command failed");
+ free_decode_response(decode);
+ return (-1);
+ }
+ if (decode->rtp_info == NULL) {
+ player_error_message("No rtp info field");
+ } else {
+ player_debug_message("rtp info is \'%s\'", decode->rtp_info);
+ }
+ int ret = pdp_process_rtsp_rtpinfo(decode->rtp_info, this, NULL);
+ free_decode_response(decode);
+ if (ret < 0) {
+ if (errmsg != NULL) {
+ snprintf(errmsg, errlen, "RTSP aggregate RtpInfo response failure");
+ }
+ player_debug_message("rtsp aggregate rtpinfo failed");
+ return (-1);
+ }
+ }
+
+ while (p != NULL) {
+ ret = p->do_play(start_time, errmsg, errlen);
+ if (ret != 0) return (ret);
+ p = p->get_next();
+ }
+ return (0);
+}
+
+/*
+ * pause_all_media - do a spin loop until the sync thread indicates it's
+ * paused.
+ */
+int CPlayerSession::pause_all_media (void)
+{
+ int ret;
+ CPlayerMedia *p;
+ m_session_state = SESSION_PAUSED;
+ if (session_control_is_aggregate()) {
+ rtsp_command_t cmd;
+ rtsp_decode_t *decode;
+
+ memset(&cmd, 0, sizeof(rtsp_command_t));
+ if (rtsp_send_aggregate_pause(m_rtsp_client,
+ m_sdp_info->control_string,
+ &cmd,
+ &decode) != 0) {
+ player_debug_message("RTSP aggregate pause command failed");
+ free_decode_response(decode);
+ return (-1);
+ }
+ free_decode_response(decode);
+ }
+ p = m_my_media;
+ while (p != NULL) {
+ ret = p->do_pause();
+ if (ret != 0) return (ret);
+ p = p->get_next();
+ }
+ m_sync_pause_done = 0;
+ send_sync_thread_a_message(MSG_PAUSE_SESSION);
+#ifndef NEED_SDL_VIDEO_IN_MAIN_THREAD
+ do {
+#endif
+ SDL_Delay(100);
+#ifndef NEED_SDL_VIDEO_IN_MAIN_THREAD
+ } while (m_sync_pause_done == 0);
+#endif
+ m_paused = 1;
+ return (0);
+}
+void CPlayerSession::add_media (CPlayerMedia *m)
+{
+ CPlayerMedia *p;
+ if (m_my_media == NULL) {
+ m_my_media = m;
+ } else {
+ p = m_my_media;
+ while (p->get_next() != NULL) {
+ if (p == m) return;
+ p = p->get_next();
+ }
+ p->set_next(m);
+ }
+}
+
+int CPlayerSession::session_has_audio (void)
+{
+ CPlayerMedia *p;
+ p = m_my_media;
+ while (p != NULL) {
+ if (p->is_video() == FALSE) {
+ return (1);
+ }
+ p = p->get_next();
+ }
+ return (0);
+}
+
+int CPlayerSession::session_has_video (void)
+{
+ CPlayerMedia *p;
+ p = m_my_media;
+ while (p != NULL) {
+ if (p->is_video() != FALSE) {
+ return (1);
+ }
+ p = p->get_next();
+ }
+ return (0);
+}
+
+void CPlayerSession::set_audio_volume (int volume)
+{
+ m_audio_volume = volume;
+ if (m_audio_sync) {
+ m_audio_sync->set_volume(m_audio_volume);
+ }
+}
+
+double CPlayerSession::get_max_time (void)
+{
+ CPlayerMedia *p;
+ double max = 0.0;
+ p = m_my_media;
+ while (p != NULL) {
+ double temp = p->get_max_playtime();
+ if (temp > max) max = temp;
+ p = p->get_next();
+ }
+ return (max);
+}
+
+/*
+ * Matches a url with the corresponding media.
+ * Return the media, or NULL if no match.
+ */
+CPlayerMedia *CPlayerSession::rtsp_url_to_media (const char *url)
+{
+ CPlayerMedia *p = m_my_media;
+ while (p != NULL) {
+ rtsp_session_t *session = p->get_rtsp_session();
+ if (rtsp_is_url_my_stream(session, url, m_content_base,
+ m_session_name) == 1)
+ return p;
+ p = p->get_next();
+ }
+ return (NULL);
+}
+
+int CPlayerSession::set_session_desc (int line, const char *desc)
+{
+ if (line >= SESSION_DESC_COUNT) {
+ return -1;
+ }
+ if (m_session_desc[line] != NULL) free((void *)m_session_desc[line]);
+ m_session_desc[line] = strdup(desc);
+ if (m_session_desc[line] == NULL)
+ return -1;
+ return (0);
+}
+
+const char *CPlayerSession::get_session_desc (int line)
+{
+ return m_session_desc[line];
+}
+/*
+ * audio_is_ready - when the audio indicates that it's ready, it will
+ * send a latency number, and a play time
+ */
+void CPlayerSession::audio_is_ready (uint64_t latency, uint64_t time)
+{
+ m_start = get_time_of_day();
+ sync_message(LOG_DEBUG, "Aisready "U64, m_start);
+ m_start -= time;
+ m_latency = latency;
+ if (latency != 0) {
+ m_clock_wrapped = -1;
+ }
+ sync_message(LOG_DEBUG, "Audio is ready "U64" - latency "U64, time, latency);
+ sync_message(LOG_DEBUG, "m_start is "X64, m_start);
+ m_waiting_for_audio = 0;
+ SDL_SemPost(m_sync_sem);
+}
+
+void CPlayerSession::adjust_start_time (int64_t time)
+{
+ m_start -= time;
+ m_clock_wrapped = -1;
+#if 0
+ sync_message(LOG_INFO, "Adjusting start time "LLD " to " LLU, time,
+ get_current_time());
+#endif
+ SDL_SemPost(m_sync_sem);
+}
+
+/*
+ * get_current_time. Gets the time of day, subtracts off the start time
+ * to get the current play time.
+ */
+uint64_t CPlayerSession::get_current_time (void)
+{
+ uint64_t current_time;
+
+ if (m_waiting_for_audio != 0) {
+ return 0;
+ }
+ current_time = get_time_of_day();
+#if 0
+ sync_message(LOG_DEBUG, "current time %llx m_start %llx",
+ current_time, m_start);
+ if (current_time < m_start) {
+ if (m_clock_wrapped == -1) {
+ return (0);
+ } else {
+ m_clock_wrapped = 1;
+ }
+ } else{
+ if (m_clock_wrapped > 0) {
+ uint64_t temp;
+ temp = 1;
+ temp <<= 32;
+ temp /= 1000;
+ current_time += temp;
+ } else {
+ m_clock_wrapped = 0;
+ }
+ }
+#endif
+ //if (current_time < m_start) return 0;
+ m_current_time = current_time - m_start;
+ if (m_current_time >= m_latency) m_current_time -= m_latency;
+ return(m_current_time);
+}
+
+void CPlayerSession::syncronize_rtp_bytestreams (rtcp_sync_t *sync)
+{
+ if (sync != NULL) {
+ m_audio_rtcp_sync = *sync;
+ m_have_audio_rtcp_sync = true;
+ } else {
+ if (!m_have_audio_rtcp_sync)
+ return;
+ }
+ CPlayerMedia *mptr = m_my_media;
+ while (mptr != NULL) {
+ if (mptr->is_video()) {
+ mptr->syncronize_rtp_bytestreams(&m_audio_rtcp_sync);
+ }
+ mptr = mptr->get_next();
+ }
+}
+
+int CPlayerSession::process_msg_queue (int state)
+{
+ CMsg *newmsg;
+ while ((newmsg = m_sync_thread_msg_queue.get_message()) != NULL) {
+#ifdef DEBUG_SYNC_MSGS
+ sync_message(LOG_DEBUG, "Sync thread msg %d", newmsg->get_value());
+#endif
+ switch (newmsg->get_value()) {
+ case MSG_PAUSE_SESSION:
+ state = SYNC_STATE_PAUSED;
+ break;
+ case MSG_START_SESSION:
+ state = SYNC_STATE_WAIT_SYNC;
+ break;
+ case MSG_STOP_THREAD:
+ state = SYNC_STATE_EXIT;
+ break;
+ default:
+ sync_message(LOG_ERR, "Sync thread received message %d",
+ newmsg->get_value());
+ break;
+ }
+ delete newmsg;
+ newmsg = NULL;
+ }
+ return (state);
+}
+
+/***************************************************************************
+ * Sync thread state handlers
+ ***************************************************************************/
+
+/*
+ * sync_thread_init - wait until all the sync routines are initialized.
+ */
+int CPlayerSession::sync_thread_init (void)
+{
+ int ret = 1;
+ for (CPlayerMedia *mptr = m_my_media;
+ mptr != NULL && ret == 1;
+ mptr = mptr->get_next()) {
+ if (mptr->is_video()) {
+ ret = m_video_sync->initialize_video(m_session_name);
+ } else {
+ ret = m_audio_sync->initialize_audio(m_video_sync != NULL);
+ }
+ }
+ if (ret == -1) {
+ sync_message(LOG_CRIT, "Fatal error while initializing hardware");
+ if (m_video_sync != NULL) {
+ m_video_sync->flush_sync_buffers();
+ }
+ if (m_audio_sync != NULL) {
+ m_audio_sync->flush_sync_buffers();
+ }
+ m_master_msg_queue->send_message(MSG_RECEIVED_QUIT,
+ NULL,
+ 0,
+ m_master_msg_queue_sem);
+ m_hardware_error = 1;
+ return (SYNC_STATE_DONE);
+ }
+
+ if (ret == 1) {
+ return (SYNC_STATE_WAIT_SYNC);
+ }
+ CMsg *newmsg;
+
+ newmsg = m_sync_thread_msg_queue.get_message();
+ if (newmsg != NULL) {
+ int value = newmsg->get_value();
+ delete newmsg;
+
+ if (value == MSG_STOP_THREAD) {
+ return (SYNC_STATE_EXIT);
+ }
+ if (value == MSG_PAUSE_SESSION) {
+ m_sync_pause_done = 1;
+ }
+ }
+
+ SDL_Delay(100);
+
+ return (SYNC_STATE_INIT);
+}
+
+/*
+ * sync_thread_wait_sync - wait until all decoding threads have put
+ * data into the sync classes.
+ */
+int CPlayerSession::sync_thread_wait_sync (void)
+{
+ int state;
+
+ state = process_msg_queue(SYNC_STATE_WAIT_SYNC);
+ if (state == SYNC_STATE_WAIT_SYNC) {
+
+ // We're not synced. See if the video is ready, and if the audio
+ // is ready. Then start them going...
+ int vsynced = 1, asynced = 1;
+ uint64_t astart, vstart;
+
+ if (m_video_sync) {
+ vsynced = m_video_sync->is_video_ready(vstart);
+ }
+
+ if (m_audio_sync) {
+ asynced = m_audio_sync->is_audio_ready(astart);
+ }
+ if (vsynced == 1 && asynced == 1) {
+ /*
+ * Audio and video are synced.
+ */
+ if (m_audio_sync) {
+ /*
+ * If we have audio, we use that for syncing. Start it up
+ */
+ m_first_time_played = astart;
+ m_current_time = astart;
+ sync_message(LOG_DEBUG, "Astart is %llu", astart);
+ m_waiting_for_audio = 1;
+ state = SYNC_STATE_WAIT_AUDIO;
+ m_audio_sync->play_audio();
+ } else if (m_video_sync) {
+ /*
+ * Video only - set up the start time based on the video time
+ * returned
+ */
+ m_first_time_played = vstart;
+ m_current_time = vstart;
+ m_waiting_for_audio = 0;
+ m_start = get_time_of_day();
+ m_start -= m_current_time;
+ state = SYNC_STATE_PLAYING;
+ }
+ sync_message(LOG_DEBUG,
+ "Resynced at time "U64 " "U64, m_current_time, vstart);
+ } else {
+ SDL_Delay(10);
+ }
+ }
+ return (state);
+}
+
+/*
+ * sync_thread_wait_audio - wait until the audio thread starts and signals
+ * us.
+ */
+int CPlayerSession::sync_thread_wait_audio (void)
+{
+ int state;
+
+ state = process_msg_queue(SYNC_STATE_WAIT_AUDIO);
+ if (state == SYNC_STATE_WAIT_AUDIO) {
+ if (m_waiting_for_audio != 0) {
+ SDL_SemWaitTimeout(m_sync_sem, 10);
+ } else {
+ // make sure we set the current time
+ get_current_time();
+ sync_message(LOG_DEBUG, "Current time is %llu", m_current_time);
+ return (SYNC_STATE_PLAYING);
+ }
+ }
+ return (state);
+}
+
+/*
+ * sync_thread_playing - do the work of displaying the video and making
+ * sure we're in sync.
+ */
+int CPlayerSession::sync_thread_playing (void)
+{
+ int state;
+ uint64_t audio_resync_time = 0;
+ int64_t video_status = 0;
+ int have_audio_eof = 0, have_video_eof = 0;
+
+ state = process_msg_queue(SYNC_STATE_PLAYING);
+ if (state == SYNC_STATE_PLAYING) {
+ get_current_time();
+ if (m_audio_sync) {
+ audio_resync_time = m_audio_sync->check_audio_sync(m_current_time,
+ have_audio_eof);
+ }
+ if (m_video_sync) {
+ video_status = m_video_sync->play_video_at(m_current_time,
+ have_video_eof);
+ }
+
+ int delay = 9;
+ int wait_for_signal = 0;
+
+ if (m_video_sync && m_audio_sync) {
+ if (have_video_eof && have_audio_eof) {
+ return (SYNC_STATE_DONE);
+ }
+ if (video_status > 0 || audio_resync_time != 0) {
+ if (audio_resync_time != 0) {
+ int64_t diff = audio_resync_time - m_current_time;
+ delay = (int)MIN(diff, video_status);
+ } else {
+ if (video_status < 9) {
+ delay = 0;
+ }
+ }
+ if (delay < 9) {
+ wait_for_signal = 0;
+ } else {
+ wait_for_signal = 1;
+ }
+ } else {
+ wait_for_signal = 0;
+ }
+ } else if (m_video_sync) {
+ if (have_video_eof == 1) {
+ return (SYNC_STATE_DONE);
+ }
+ if (video_status >= 9) {
+ wait_for_signal = 1;
+ delay = (int)video_status;
+ } else {
+ wait_for_signal = 0;
+ }
+ } else {
+ // audio only
+ if (have_audio_eof == 1) {
+ return (SYNC_STATE_DONE);
+ }
+ if (audio_resync_time != 0) {
+ if (audio_resync_time - m_current_time > 10) {
+ wait_for_signal = 1;
+ delay = (int)(audio_resync_time - m_current_time);
+ } else {
+ wait_for_signal = 0;
+ }
+ } else {
+ wait_for_signal = 1;
+ }
+ }
+ //player_debug_message("w %d d %d", wait_for_signal, delay);
+ if (wait_for_signal) {
+ if (delay > 9) {
+ delay = 9;
+ }
+ //player_debug_message("sw %d", delay);
+
+ SDL_SemWaitTimeout(m_sync_sem, delay);
+ }
+ }
+ return (state);
+}
+
+/*
+ * sync_thread_pause - wait for messages to continue or finish
+ */
+int CPlayerSession::sync_thread_paused (void)
+{
+ int state;
+ state = process_msg_queue(SYNC_STATE_PAUSED);
+ if (state == SYNC_STATE_PAUSED) {
+ SDL_SemWaitTimeout(m_sync_sem, 10);
+ }
+ return (state);
+}
+
+/*
+ * sync_thread_pause - wait for messages to exit, pretty much.
+ */
+int CPlayerSession::sync_thread_done (void)
+{
+ int state;
+ state = process_msg_queue(SYNC_STATE_DONE);
+ if (state == SYNC_STATE_DONE) {
+ SDL_SemWaitTimeout(m_sync_sem, 10);
+ }
+ return (state);
+}
+
+/*
+ * sync_thread - call the correct handler, and wait for the state changes.
+ * Each handler should only return the new state...
+ */
+int CPlayerSession::sync_thread (int state)
+{
+ int newstate = 0;
+ switch (state) {
+ case SYNC_STATE_INIT:
+ m_session_state = SESSION_BUFFERING;
+ newstate = sync_thread_init();
+ break;
+ case SYNC_STATE_WAIT_SYNC:
+ newstate = sync_thread_wait_sync();
+ break;
+ case SYNC_STATE_WAIT_AUDIO:
+ newstate = sync_thread_wait_audio();
+ break;
+ case SYNC_STATE_PLAYING:
+ newstate = sync_thread_playing();
+ break;
+ case SYNC_STATE_PAUSED:
+ newstate = sync_thread_paused();
+ break;
+ case SYNC_STATE_DONE:
+ newstate = sync_thread_done();
+ break;
+ }
+#ifdef DEBUG_SYNC_STATE
+ if (state != newstate)
+ sync_message(LOG_INFO, "sync changed state %s to %s",
+ sync_state[state], sync_state[newstate]);
+#endif
+ if (state != newstate) {
+ state = newstate;
+ switch (state) {
+ case SYNC_STATE_WAIT_SYNC:
+ m_session_state = SESSION_BUFFERING;
+ break;
+ case SYNC_STATE_WAIT_AUDIO:
+ break;
+ case SYNC_STATE_PLAYING:
+ m_session_state = SESSION_PLAYING;
+ break;
+ case SYNC_STATE_PAUSED:
+ if (m_video_sync != NULL)
+ m_video_sync->flush_sync_buffers();
+ if (m_audio_sync != NULL)
+ m_audio_sync->flush_sync_buffers();
+ m_session_state = SESSION_PAUSED;
+ m_sync_pause_done = 1;
+ break;
+ case SYNC_STATE_DONE:
+ m_master_msg_queue->send_message(MSG_SESSION_FINISHED,
+ NULL,
+ 0,
+ m_master_msg_queue_sem);
+ m_session_state = SESSION_DONE;
+ break;
+ case SYNC_STATE_EXIT:
+ if (m_video_sync != NULL)
+ m_video_sync->flush_sync_buffers();
+ if (m_audio_sync != NULL)
+ m_audio_sync->flush_sync_buffers();
+ break;
+ }
+ }
+ return (state);
+}
+
+int pdp_sync_thread (void *data)
+{
+ CPlayerSession *p;
+ int state = SYNC_STATE_INIT;
+ p = (CPlayerSession *)data;
+ do {
+ state = p->sync_thread(state);
+ } while (state != SYNC_STATE_EXIT);
+ return (0);
+}
diff --git a/modules/pdp_mp4player~.cpp b/modules/pdp_mp4player~.cpp
new file mode 100644
index 0000000..613f673
--- /dev/null
+++ b/modules/pdp_mp4player~.cpp
@@ -0,0 +1,384 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This object is a quicktime stream picker object
+ * A lot of this object code is inspired by the code from mpeg4ip
+ * Copyright (c) 2000, 2001, 2002 Dave Mackie, Bill May & others
+ * The rest is written by Yves Degoyon ( ydegoyon@free.fr )
+ */
+
+
+#include "pdp_mp4player~.h"
+
+static char *pdp_mp4player_version = "pdp_mp4player~: version 0.1, a mpeg4ip stream decoder ( ydegoyon@free.fr).";
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+static void pdp_mp4player_audio(t_pdp_mp4player *x, t_floatarg faudio )
+{
+ if ( ( faudio == 0. ) || ( faudio == 1. ) )
+ {
+ x->x_audio = (int)faudio;
+ }
+}
+
+static void pdp_mp4player_overtcp(t_pdp_mp4player *x, t_floatarg fovertcp )
+{
+ if ( ( fovertcp == 0. ) || ( fovertcp == 1. ) )
+ {
+ x->x_rtpovertcp = (t_int)fovertcp;
+ config.set_config_value(CONFIG_USE_RTP_OVER_RTSP, x->x_rtpovertcp);
+ if ( x->x_rtpovertcp )
+ {
+ post("pdp_mp4player~ : using rtp over rtsp (tcp)" );
+ }
+ else
+ {
+ post("pdp_mp4player~ : using rtp mode (udp)" );
+ }
+ }
+}
+
+static void pdp_mp4player_priority(t_pdp_mp4player *x, t_floatarg fpriority )
+{
+ if ( ( x->x_priority >= MIN_PRIORITY ) && ( x->x_priority <= MAX_PRIORITY ) )
+ {
+ x->x_priority = (int)fpriority;
+ }
+}
+
+static void pdp_mp4player_vwidth(t_pdp_mp4player *x, t_floatarg fWidth )
+{
+ if ( ( (t_int) fWidth <= 0 ) )
+ {
+ post("pdp_mp4player~ : wrong width : %d", fWidth );
+ return;
+ }
+
+ post( "pdp_mp4player~ : setting width : %d", (t_int) fWidth );
+ config.set_config_value( CONFIG_VIDEO_RAW_WIDTH, (t_int) fWidth );
+
+}
+
+static void pdp_mp4player_vheight(t_pdp_mp4player *x, t_floatarg fHeight )
+{
+ if ( ( (t_int) fHeight <= 0 ) )
+ {
+ post("pdp_mp4player~ : wrong height : %d", fHeight );
+ return;
+ }
+
+ post( "pdp_mp4player~ : setting height : %d", (t_int) fHeight );
+ config.set_config_value( CONFIG_VIDEO_RAW_HEIGHT, (t_int) fHeight );
+
+}
+
+static void pdp_mp4player_disconnect(t_pdp_mp4player *x)
+{
+ if (!x->x_streaming)
+ {
+ post("pdp_mp4player~ : close request but no stream is played ... ignored" );
+ return;
+ }
+
+ x->x_streaming = 0;
+
+ outlet_float( x->x_outlet_streaming, x->x_streaming );
+ x->x_nbframes = 0;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+
+ post( "pdp_mp4player~ : deleting session" );
+ delete x->x_psession;
+ post( "pdp_mp4player~ : deleting semaphore" );
+ SDL_DestroySemaphore(x->x_psem);
+}
+
+static void *pdp_mp4player_decode(void *tdata)
+{
+ t_pdp_mp4player *x = (t_pdp_mp4player*)tdata;
+ struct sched_param schedprio;
+ t_int pmin, pmax;
+ struct timespec twait, mwait;
+
+ twait.tv_sec = 0;
+ twait.tv_nsec = 10000000; // 10 ms
+
+ schedprio.sched_priority = 0;
+ if ( sched_setscheduler(0, SCHED_OTHER, &schedprio) == -1)
+ {
+ post("pdp_mp4player~ : couldn't set scheduler for decoding thread.\n");
+ }
+ if ( setpriority( PRIO_PROCESS, 0, x->x_priority ) < 0 )
+ {
+ post("pdp_mp4player~ : couldn't set priority to %d for decoding thread.\n", x->x_priority );
+ }
+ else
+ {
+ post("pdp_mp4player~ : priority set to %d for thread %d.\n", x->x_priority, x->x_decodechild );
+ }
+
+ while ( x->x_streaming )
+ {
+ x->x_decodingstate = x->x_psession->sync_thread(x->x_decodingstate);
+ nanosleep( &twait, NULL ); // nothing to read, just wait
+ }
+
+ post( "pdp_mp4player~ : decoding thread %d exiting....", x->x_decodechild );
+ x->x_decodechild = 0;
+ pthread_exit(NULL);
+}
+
+
+static void pdp_mp4player_connect(t_pdp_mp4player *x, t_symbol *s)
+{
+ t_int ret, i;
+ char buffer[1024];
+ char errmsg[512];
+ pthread_attr_t decode_child_attr;
+
+ if ( x->x_streaming )
+ {
+ post("pdp_mp4player~ : connection request but a connection is pending ... disconnecting" );
+ pdp_mp4player_disconnect(x);
+ }
+
+ if ( x->x_url ) free( x->x_url );
+ x->x_url = (char*) malloc( strlen( s->s_name ) + 1 );
+ strcpy( x->x_url, s->s_name );
+
+ x->x_psem = SDL_CreateSemaphore(0);
+ snprintf(buffer, sizeof(buffer), "pdp_mp4player~ - %s", x->x_url);
+ x->x_psession = new CPlayerSession(&x->x_queue, x->x_psem, buffer, x);
+ if (x->x_psession == NULL)
+ {
+ post("pdp_mp4player~ : FATAL : could not create session" );
+ return;
+ }
+
+ ret = parse_name_for_session(x->x_psession, x->x_url, errmsg, sizeof(errmsg), NULL);
+ if (ret < 0)
+ {
+ post("pdp_mp4player~ : FATAL : wrong url : %s : reason : %s", x->x_url, errmsg );
+ delete x->x_psession;
+ return;
+ }
+
+ if (ret > 0)
+ {
+ post("pdp_mp4player~ : %s", errmsg );
+ }
+
+ x->x_psession->set_up_sync_thread();
+
+ if (x->x_psession->play_all_media(TRUE) != 0) {
+ post("pdp_mp4player~ : FATAL : couldn't play all medias" );
+ delete x->x_psession;
+ return;
+ }
+
+ // launch decoding thread
+ if ( pthread_attr_init( &decode_child_attr ) < 0 )
+ {
+ post( "pdp_mp4player~ : could not launch decoding thread" );
+ perror( "pthread_attr_init" );
+ return;
+ }
+ if ( pthread_create( &x->x_decodechild, &decode_child_attr, pdp_mp4player_decode, x ) < 0 )
+ {
+ post( "pdp_mp4player~ : could not launch decoding thread" );
+ perror( "pthread_create" );
+ return;
+ }
+
+ post("pdp_mp4player~ : session started" );
+ x->x_streaming = 1;
+
+ return;
+}
+
+ /* decode the stream to fill up buffers */
+static t_int *pdp_mp4player_perform(t_int *w)
+{
+ t_float *out1 = (t_float *)(w[1]); // left audio inlet
+ t_float *out2 = (t_float *)(w[2]); // right audio inlet
+ t_pdp_mp4player *x = (t_pdp_mp4player *)(w[3]);
+ int n = (int)(w[4]); // number of samples
+ short sampleL, sampleR;
+ struct timeval etime;
+ t_int sn;
+
+ // just read the buffer
+ if ( x->x_audioon )
+ {
+ sn=0;
+ n=n*DEFAULT_CHANNELS;
+ while (n--)
+ {
+ sampleL=x->x_audio_in[ sn++ ];
+ *(out1) = ((t_float)sampleL)/32768.0;
+ if ( DEFAULT_CHANNELS == 1 )
+ {
+ *(out2) = *(out1);
+ }
+ if ( DEFAULT_CHANNELS == 2 )
+ {
+ sampleR=x->x_audio_in[ sn++ ];
+ *(out2) = ((t_float)sampleR)/32768.0;
+ }
+ out1++;
+ out2++;
+ }
+ x->x_audioin_position-=sn;
+ memcpy( &x->x_audio_in[0], &x->x_audio_in[sn], 4*MAX_AUDIO_PACKET_SIZE-sn );
+ // post( "pdp_mp4player~ : audio in position : %d", x->x_audioin_position );
+ if ( x->x_audioin_position <= sn )
+ {
+ x->x_audioon = 0;
+ // post( "pdp_mp4player~ : audio off" );
+ }
+ }
+ else
+ {
+ // post("pdp_mp4player~ : no available audio" );
+ while (n--)
+ {
+ *(out1++) = 0.0;
+ *(out2++) = 0.0;
+ }
+ }
+
+ // check if the framerate has been exceeded
+ if ( gettimeofday(&etime, NULL) == -1)
+ {
+ post("pdp_mp4player~ : could not read time" );
+ }
+ if ( etime.tv_sec != x->x_cursec )
+ {
+ x->x_cursec = etime.tv_sec;
+ outlet_float( x->x_outlet_framerate, x->x_secondcount );
+ x->x_secondcount = 0;
+ }
+
+ if ( x->x_newpicture )
+ {
+ pdp_packet_pass_if_valid(x->x_pdp_out, &x->x_packet0);
+
+ // update streaming status
+ outlet_float( x->x_outlet_streaming, x->x_streaming );
+ x->x_nbframes++;
+ x->x_secondcount++;
+ outlet_float( x->x_outlet_nbframes, x->x_nbframes );
+ }
+
+ return (w+5);
+}
+
+static void pdp_mp4player_dsp(t_pdp_mp4player *x, t_signal **sp)
+{
+ dsp_add(pdp_mp4player_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n);
+}
+
+static void pdp_mp4player_free(t_pdp_mp4player *x)
+{
+ int i;
+
+ if ( x->x_streaming )
+ {
+ pdp_mp4player_disconnect(x);
+ }
+ post( "pdp_mp4player~ : freeing object" );
+ pdp_packet_mark_unused(x->x_packet0);
+
+ // remove invalid global ports
+ close_plugins();
+}
+
+t_class *pdp_mp4player_class;
+
+void *pdp_mp4player_new(void)
+{
+ int i;
+
+ t_pdp_mp4player *x = (t_pdp_mp4player *)pd_new(pdp_mp4player_class);
+
+ x->x_pdp_out = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_outlet_left = outlet_new(&x->x_obj, &s_signal);
+ x->x_outlet_right = outlet_new(&x->x_obj, &s_signal);
+
+ x->x_outlet_streaming = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_nbframes = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_framerate = outlet_new(&x->x_obj, &s_float);
+
+ x->x_packet0 = -1;
+ x->x_nbframes = 0;
+ x->x_cursec = 0;
+ x->x_secondcount = 0;
+ x->x_audioin_position = 0;
+ x->x_priority = DEFAULT_PRIORITY;
+ x->x_decodechild = 0;
+ x->x_newpicture = 0;
+
+ memset( &x->x_audio_buf[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) );
+ memset( &x->x_audio_in[0], 0x0, 4*MAX_AUDIO_PACKET_SIZE*sizeof(short) );
+
+ // initialize mpeg4hippies
+ initialize_plugins();
+ config.read_config_file();
+ rtsp_set_error_func(player_library_message);
+ rtsp_set_loglevel(config.get_config_value(CONFIG_RTSP_DEBUG));
+ rtp_set_error_msg_func(player_library_message);
+ rtp_set_loglevel(config.get_config_value(CONFIG_RTP_DEBUG));
+ sdp_set_error_func(player_library_message);
+ sdp_set_loglevel(config.get_config_value(CONFIG_SDP_DEBUG));
+ http_set_error_func(player_library_message);
+ http_set_loglevel(config.get_config_value(CONFIG_HTTP_DEBUG));
+
+ x->x_rtpovertcp = 0;
+ config.set_config_value(CONFIG_USE_RTP_OVER_RTSP, x->x_rtpovertcp);
+
+ return (void *)x;
+}
+
+
+void pdp_mp4player_tilde_setup(void)
+{
+ // post( pdp_mp4player_version );
+ pdp_mp4player_class = class_new(gensym("pdp_mp4player~"), (t_newmethod)pdp_mp4player_new,
+ (t_method)pdp_mp4player_free, sizeof(t_pdp_mp4player), 0, A_NULL);
+
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_dsp, gensym("dsp"), A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_connect, gensym("connect"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_disconnect, gensym("disconnect"), A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_audio, gensym("audio"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_overtcp, gensym("overtcp"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_priority, gensym("priority"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_vwidth, gensym("vwidth"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_mp4player_class, (t_method)pdp_mp4player_vheight, gensym("vheight"), A_DEFFLOAT, A_NULL);
+ class_sethelpsymbol( pdp_mp4player_class, gensym("pdp_mp4player~.pd") );
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/pdp_mp4videosource.cpp b/modules/pdp_mp4videosource.cpp
new file mode 100644
index 0000000..5e5ef91
--- /dev/null
+++ b/modules/pdp_mp4videosource.cpp
@@ -0,0 +1,185 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dave Mackie dmackie@cisco.com
+ * Bill May wmay@cisco.com
+ *
+ * Adapted for PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+#ifndef debug_message
+#define debug_message post
+#endif
+
+#include <sys/mman.h>
+
+#include "m_pd.h"
+#include "pdp_mp4videosource.h"
+#include "video_util_rgb.h"
+
+//#define DEBUG_TIMESTAMPS 1
+
+int CPDPVideoSource::ThreadMain(void)
+{
+ // just a stub, the threaded mode is not used ( never called )
+ return 0;
+}
+
+void CPDPVideoSource::DoStart(void)
+{
+ if (m_source) return;
+ if (!Init()) return;
+ m_source = true;
+}
+
+void CPDPVideoSource::DoStop(void)
+{
+ if (!m_source) return;
+ DoStopVideo();
+ m_source = false;
+}
+
+bool CPDPVideoSource::Init(void)
+{
+
+ if (m_pConfig->GetIntegerValue(CONFIG_VIDEO_SIGNAL) == VIDEO_SIGNAL_NTSC)
+ {
+ m_videoSrcFrameRate = VIDEO_NTSC_FRAME_RATE;
+ }
+ else
+ {
+ m_videoSrcFrameRate = VIDEO_PAL_FRAME_RATE;
+ }
+
+ m_videoMbuf.frames = 2;
+ m_cacheTimestamp = false;
+ m_videoFrameMap = (struct video_mmap*) malloc(m_videoMbuf.frames * sizeof(struct video_mmap));
+ if ( !m_videoFrameMap )
+ {
+ post("pdp_mp4live~ : video source init : failed to allocate enough memory");
+ return false;
+ }
+
+ m_videoFrameMapFrame = (uint64_t *)malloc(m_videoMbuf.frames * sizeof(uint64_t));
+ m_videoFrameMapTimestamp = (uint64_t *)malloc(m_videoMbuf.frames * sizeof(Timestamp));
+ m_captureHead = 0;
+ m_encodeHead = -1;
+
+ m_videoFrames = 0;
+ m_videoSrcFrameDuration = (Duration)(((float)TimestampTicks / m_videoSrcFrameRate) + 0.5);
+ for (int i = 0; i < m_videoMbuf.frames; i++)
+ {
+ // initialize frame map
+ m_videoFrameMap[i].frame = i;
+ m_videoFrameMap[i].width = m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH);
+ m_videoFrameMap[i].height = m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT);
+ m_videoFrameMap[i].format = VIDEO_PALETTE_YUV420P;
+
+ if (i == 0)
+ {
+ m_videoCaptureStartTimestamp = GetTimestamp();
+ }
+ m_lastVideoFrameMapFrameLoaded = m_videoFrameMapFrame[i] = i;
+ m_lastVideoFrameMapTimestampLoaded =
+ m_videoFrameMapTimestamp[i] = CalculateVideoTimestampFromFrames(i);
+ }
+
+ m_pConfig->CalculateVideoFrameSize();
+
+ InitVideo(
+ (m_pConfig->m_videoNeedRgbToYuv ?
+ RGBVIDEOFRAME :
+ YUVVIDEOFRAME),
+ true);
+
+ SetVideoSrcSize(
+ m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH),
+ m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_HEIGHT),
+ m_pConfig->GetIntegerValue(CONFIG_VIDEO_RAW_WIDTH),
+ true);
+
+ m_maxPasses = (u_int8_t)(m_videoSrcFrameRate + 0.5);
+
+ return true;
+}
+
+int8_t CPDPVideoSource::StartTimeStamp(Timestamp &frameTimestamp)
+{
+ if (m_cacheTimestamp)
+ frameTimestamp = m_videoFrameMapTimestamp[m_captureHead];
+ else
+ frameTimestamp = GetTimestamp();
+
+ int8_t capturedFrame = m_captureHead;
+ m_captureHead = (m_captureHead + 1) % m_videoMbuf.frames;
+
+ return capturedFrame;
+}
+
+bool CPDPVideoSource::EndTimeStamp(int8_t frameNumber)
+{
+ Timestamp calc = GetTimestamp();
+
+ if (calc > m_videoSrcFrameDuration + m_lastVideoFrameMapTimestampLoaded) {
+#ifdef DEBUG_TIMESTAMPS
+ debug_message("pdp_mp4live~ : video frame delay past end of buffer - time is %llu should be %llu",
+ calc,
+ m_videoSrcFrameDuration + m_lastVideoFrameMapTimestampLoaded);
+#endif
+ m_videoCaptureStartTimestamp = calc;
+ m_videoFrameMapFrame[frameNumber] = 0;
+ m_videoFrameMapTimestamp[frameNumber] = calc;
+ } else {
+ m_videoFrameMapFrame[frameNumber] = m_lastVideoFrameMapFrameLoaded + 1;
+ m_videoFrameMapTimestamp[frameNumber] =
+ CalculateVideoTimestampFromFrames(m_videoFrameMapFrame[frameNumber]);
+ }
+
+ m_lastVideoFrameMapFrameLoaded = m_videoFrameMapFrame[frameNumber];
+ m_lastVideoFrameMapTimestampLoaded = m_videoFrameMapTimestamp[frameNumber];
+ return true;
+}
+
+void CPDPVideoSource::ProcessVideo(u_int8_t *pY, u_int8_t *pU, u_int8_t *pV)
+{
+ // for efficiency, process ~1 second before returning to check for commands
+ Timestamp frameTimestamp;
+ for (int pass = 0; pass < m_maxPasses; pass++) {
+
+ // get next frame from video capture device
+ m_encodeHead = StartTimeStamp(frameTimestamp);
+ if (m_encodeHead == -1) {
+ continue;
+ }
+
+ ProcessVideoYUVFrame(
+ pY,
+ pU,
+ pV,
+ m_videoSrcWidth,
+ m_videoSrcWidth >> 1,
+ frameTimestamp);
+
+ // release video frame buffer back to video capture device
+ if (EndTimeStamp(m_encodeHead)) {
+ m_encodeHead = (m_encodeHead + 1) % m_videoMbuf.frames;
+ } else {
+ debug_message("pdp_mp4live~ : couldn't release capture buffer!");
+ }
+ }
+}
diff --git a/modules/pdp_mp4videosync.cpp b/modules/pdp_mp4videosync.cpp
new file mode 100644
index 0000000..b7fca2e
--- /dev/null
+++ b/modules/pdp_mp4videosync.cpp
@@ -0,0 +1,248 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is MPEG4IP.
+ *
+ * The Initial Developer of the Original Code is Cisco Systems Inc.
+ * Portions created by Cisco Systems Inc. are
+ * Copyright (C) Cisco Systems Inc. 2000, 2001. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Bill May wmay@cisco.com
+ * video aspect ratio by:
+ * Peter Maersk-Moller peter@maersk-moller.net
+ *
+ * Adapted to PD/PDP by Yves Degoyon (ydegoyon@free.fr)
+ */
+
+/*
+ * video.cpp - provides codec to video hardware class
+ */
+
+#include <string.h>
+#include "pdp_mp4videosync.h"
+#include "pdp_mp4playersession.h"
+#include "player_util.h"
+#include "m_pd.h"
+
+#define video_message(loglevel, fmt...) message(loglevel, "videosync", fmt)
+
+CPDPVideoSync::CPDPVideoSync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father) : CVideoSync(psptr)
+{
+ char buf[32];
+
+ m_screen = NULL;
+ m_image = NULL;
+ m_video_initialized = 0;
+ m_config_set = 0;
+ m_have_data = 0;
+ m_y_buffer[0] = NULL;
+ m_u_buffer[0] = NULL;
+ m_v_buffer[0] = NULL;
+ m_buffer_filled[0] = 0;
+ m_play_index = m_fill_index = 0;
+ m_decode_waiting = 0;
+ m_dont_fill = 0;
+ m_paused = 1;
+ m_behind_frames = 0;
+ m_total_frames = 0;
+ m_behind_time = 0;
+ m_behind_time_max = 0;
+ m_skipped_render = 0;
+ m_video_scale = 2;
+ m_msec_per_frame = 0;
+ m_consec_skipped = 0;
+ m_fullscreen = 0;
+ m_filled_frames = 0;
+ m_double_width = 0;
+ m_pixel_width = 0;
+ m_pixel_height = 0;
+ m_max_width = 0;
+ m_max_height = 0;
+ m_father = pdp_father;
+}
+
+CPDPVideoSync::~CPDPVideoSync (void)
+{
+ if (m_fullscreen != 0) {
+ m_fullscreen = 0;
+ do_video_resize();
+ }
+ if (m_image) {
+ m_image = NULL;
+ }
+ if (m_screen) {
+ m_screen = NULL;
+ }
+ if (m_y_buffer[0] != NULL) {
+ free(m_y_buffer[0]);
+ m_y_buffer[0] = NULL;
+ }
+ if (m_u_buffer[0] != NULL) {
+ free(m_u_buffer[0]);
+ m_u_buffer[0] = NULL;
+ }
+ if (m_v_buffer[0] != NULL) {
+ free(m_v_buffer[0]);
+ m_v_buffer[0] = NULL;
+ }
+}
+
+void CPDPVideoSync::config (int w, int h)
+{
+ m_width = w;
+ m_height = h;
+ m_y_buffer[0] = (uint8_t *)malloc(w * h * sizeof(uint8_t));
+ m_u_buffer[0] = (uint8_t *)malloc(w/2 * h/2 * sizeof(uint8_t));
+ m_v_buffer[0] = (uint8_t *)malloc(w/2 * h/2 * sizeof(uint8_t));
+ m_buffer_filled[0] = 0;
+ m_config_set = 1;
+ post( "pdp_mp4videosync : configuration done : %dx%d", m_width, m_height );
+}
+
+int CPDPVideoSync::initialize_video (const char *name)
+{
+ if (m_video_initialized == 0) {
+ if (m_config_set) {
+ int ret;
+ int video_scale = m_video_scale;
+
+ int w = m_width * video_scale / 2;
+ if (m_double_width) w *= 2;
+ int h = m_height * video_scale / 2;
+ m_video_initialized = 1;
+ post( "pdp_mp4videosync : video initialized : %dx%d", m_width, m_height );
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+ return (1);
+}
+
+int CPDPVideoSync::is_video_ready (uint64_t &disptime)
+{
+ return 1;
+}
+
+void CPDPVideoSync::play_video (void)
+{
+
+}
+
+int64_t CPDPVideoSync::play_video_at (uint64_t current_time, int &have_eof)
+{
+ uint64_t play_this_at;
+ unsigned int ix;
+ uint8_t *to, *from;
+
+ post( "pdp_mp4videosync : play video at : %ld", current_time );
+
+ return (10);
+}
+
+int CPDPVideoSync::get_video_buffer(uint8_t **y,
+ uint8_t **u,
+ uint8_t **v)
+{
+
+ post( "pdp_mp4videosync : get video buffer" );
+ *y = m_y_buffer[m_fill_index];
+ *u = m_u_buffer[m_fill_index];
+ *v = m_v_buffer[m_fill_index];
+ return (1);
+}
+
+void CPDPVideoSync::filled_video_buffers (uint64_t time)
+{
+ int ix;
+ // post( "pdp_mp4videosync : filled video buffer : %ld", time );
+ m_psptr->wake_sync_thread();
+}
+
+void CPDPVideoSync::set_video_frame(const uint8_t *y,
+ const uint8_t *u,
+ const uint8_t *v,
+ int pixelw_y,
+ int pixelw_uv,
+ uint64_t time)
+{
+ // post( "pdp_mp4videosync : set video frame : %dx%d", m_width, m_height );
+ m_psptr->wake_sync_thread();
+
+ // pass the data to the pdp object
+ m_father->x_newpicture = 1;
+ return;
+}
+
+void CPDPVideoSync::flush_sync_buffers (void)
+{
+ post( "pdp_mp4videosync : flush sync buffer" );
+}
+
+void CPDPVideoSync::flush_decode_buffers (void)
+{
+ post( "pdp_mp4videosync : flush decode buffer" );
+}
+
+static void pdp_video_configure (void *ifptr,
+ int w,
+ int h,
+ int format,
+ double aspect_ratio )
+{
+ ((CPDPVideoSync *)ifptr)->config(w, h);
+}
+
+static int pdp_video_get_buffer (void *ifptr,
+ uint8_t **y,
+ uint8_t **u,
+ uint8_t **v)
+{
+ return (((CPDPVideoSync *)ifptr)->get_video_buffer(y, u, v));
+}
+
+static void pdp_video_filled_buffer(void *ifptr, uint64_t time)
+{
+ ((CPDPVideoSync *)ifptr)->filled_video_buffers(time);
+}
+
+static void pdp_video_have_frame (void *ifptr,
+ const uint8_t *y,
+ const uint8_t *u,
+ const uint8_t *v,
+ int m_pixelw_y,
+ int m_pixelw_uv,
+ uint64_t time)
+{
+ CPDPVideoSync *foo = (CPDPVideoSync *)ifptr;
+
+ foo->set_video_frame(y, u, v, m_pixelw_y, m_pixelw_uv, time);
+}
+
+video_vft_t video_vft =
+{
+ message,
+ pdp_video_configure,
+ pdp_video_get_buffer,
+ pdp_video_filled_buffer,
+ pdp_video_have_frame,
+};
+
+video_vft_t *get_video_vft (void)
+{
+ return (&video_vft);
+}
+
+CPDPVideoSync *pdp_create_video_sync (CPlayerSession *psptr, t_pdp_mp4player *pdp_father)
+{
+ return new CPDPVideoSync(psptr, pdp_father);
+}
diff --git a/modules/pdp_spotlight.c b/modules/pdp_spotlight.c
new file mode 100644
index 0000000..e294642
--- /dev/null
+++ b/modules/pdp_spotlight.c
@@ -0,0 +1,320 @@
+/*
+ * PiDiP module.
+ * Copyright (c) by Yves Degoyon (ydegoyon@free.fr)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* This object is an adaptation of lens effect from effectv
+ * Originally written by Fukuchi Kentaro & others
+ * Pd-fication by Yves Degoyon
+ */
+
+
+
+#include "pdp.h"
+#include "yuv.h"
+#include <math.h>
+
+static char *pdp_spotlight_version = "pdp_spotlight: version 0.1, specially made for cabaret, written by Yves Degoyon (ydegoyon@free.fr)";
+
+typedef struct pdp_spotlight_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+ t_int x_packet0;
+ t_int x_packet1;
+ t_int x_dropped;
+ t_int x_queue_id;
+
+ t_int x_vwidth;
+ t_int x_vheight;
+ t_int x_vsize;
+ t_int x_cx; // coordinates of lens center
+ t_int x_cy; // coordinates of lens center
+ t_int x_ssize; // width of the spotlight
+ t_float x_strength; // strength of the light (0<=strength<=1)
+
+ t_int x_colorR; // red component of the color
+ t_int x_colorG; // green component of the color
+ t_int x_colorB; // blue component of the color
+
+} t_pdp_spotlight;
+
+static void pdp_spotlight_ssize(t_pdp_spotlight *x, t_floatarg fssize )
+{
+ if ( fssize>=0 )
+ {
+ x->x_ssize = (int)fssize;
+ }
+}
+
+static void pdp_spotlight_cy(t_pdp_spotlight *x, t_floatarg fcy )
+{
+ if ( fcy>0 )
+ {
+ x->x_cy = (int)fcy;
+ }
+}
+
+static void pdp_spotlight_cx(t_pdp_spotlight *x, t_floatarg fcx )
+{
+ if ( fcx>0 )
+ {
+ x->x_cx = (int)fcx;
+ }
+}
+
+static void pdp_spotlight_r(t_pdp_spotlight *x, t_floatarg fr )
+{
+ if ( ( fr >= 0 ) && ( fr <= 255 ) )
+ {
+ x->x_colorR = (int)fr;
+ }
+}
+
+static void pdp_spotlight_g(t_pdp_spotlight *x, t_floatarg fg )
+{
+ if ( ( fg >= 0 ) && ( fg <= 255 ) )
+ {
+ x->x_colorG = (int)fg;
+ }
+}
+
+static void pdp_spotlight_b(t_pdp_spotlight *x, t_floatarg fb )
+{
+ if ( ( fb >= 0 ) && ( fb <= 255 ) )
+ {
+ x->x_colorB = (int)fb;
+ }
+}
+
+static void pdp_spotlight_strength(t_pdp_spotlight *x, t_floatarg fstrength )
+{
+ if ( ( fstrength >= 0.0 ) && ( fstrength <= 1.0 ) )
+ {
+ x->x_strength = fstrength;
+ }
+}
+
+static void pdp_spotlight_process_yv12(t_pdp_spotlight *x)
+{
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = (short int *)pdp_packet_data(x->x_packet0);
+ t_pdp *newheader = pdp_packet_header(x->x_packet1);
+ short int *newdata = (short int *)pdp_packet_data(x->x_packet1);
+ int i;
+
+ short int *poy, *pou, *pov, *pny, *pnu, *pnv;
+ short int pmx, pMx, pmy, pMy;
+ int px, py, ray2;
+
+ x->x_vwidth = header->info.image.width;
+ x->x_vheight = header->info.image.height;
+ x->x_vsize = x->x_vwidth*x->x_vheight;
+
+ newheader->info.image.encoding = header->info.image.encoding;
+ newheader->info.image.width = x->x_vwidth;
+ newheader->info.image.height = x->x_vheight;
+
+ memcpy(newdata, data, (x->x_vsize + (x->x_vsize>>1))<<1);
+
+ poy = data;
+ pou = data + x->x_vsize;
+ pov = data + x->x_vsize + (x->x_vsize>>2);
+ pny = newdata;
+ pnu = newdata + x->x_vsize;
+ pnv = newdata + x->x_vsize + (x->x_vsize>>2);
+ if ( x->x_cy-x->x_ssize < 0 )
+ {
+ pmy=0;
+ }
+ else
+ {
+ pmy=x->x_cy-x->x_ssize;
+ }
+ if ( x->x_cy+x->x_ssize > x->x_vheight )
+ {
+ pMy=x->x_vheight-1;
+ }
+ else
+ {
+ pMy=x->x_cy+x->x_ssize;
+ }
+ if ( x->x_cx-x->x_ssize < 0 )
+ {
+ pmx=0;
+ }
+ else
+ {
+ pmx=x->x_cx-x->x_ssize;
+ }
+ if ( x->x_cx+x->x_ssize > x->x_vwidth )
+ {
+ pMx=x->x_vwidth-1;
+ }
+ else
+ {
+ pMx=x->x_cx+x->x_ssize;
+ }
+ ray2 = pow( x->x_ssize, 2 );
+ for (py = pmy; py <= pMy ; py++)
+ {
+ for (px = pmx; px <= pMx; px++)
+ {
+ if ( ( pow( (px-x->x_cx), 2 ) + pow( (py-x->x_cy), 2 ) ) < ray2 )
+ {
+ *(pny+py*x->x_vwidth+px) =
+ (t_float)(*(pny+py*x->x_vwidth+px))*(1.-x->x_strength) +
+ (t_float)(((yuv_RGBtoY( (x->x_colorB << 16) + (x->x_colorG << 8) + x->x_colorR ))<<7))
+ *(x->x_strength);
+ *(pnu+(py>>1)*(x->x_vwidth>>1)+(px>>1)) =
+ (t_float)(*(pnu+(py>>1)*(x->x_vwidth>>1)+(px>>1)))*(1.-x->x_strength) +
+ (t_float)(((yuv_RGBtoU( (x->x_colorB << 16) + (x->x_colorG << 8)
+ + x->x_colorR ))-128)<<8)*(x->x_strength);
+ *(pnv+(py>>1)*(x->x_vwidth>>1)+(px>>1)) =
+ (t_float)(*(pnv+(py>>1)*(x->x_vwidth>>1)+(px>>1)))*(1.-x->x_strength) +
+ (t_float)(((yuv_RGBtoV( (x->x_colorB << 16) + (x->x_colorG << 8)
+ + x->x_colorR ))-128)<<8)*(x->x_strength);
+ }
+ }
+ }
+
+ return;
+}
+
+static void pdp_spotlight_sendpacket(t_pdp_spotlight *x)
+{
+ /* release the packet */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+
+ /* unregister and propagate if valid dest packet */
+ pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1);
+}
+
+static void pdp_spotlight_process(t_pdp_spotlight *x)
+{
+ int encoding;
+ t_pdp *header = 0;
+
+ /* check if image data packets are compatible */
+ if ( (header = pdp_packet_header(x->x_packet0))
+ && (PDP_IMAGE == header->type)){
+
+ /* pdp_spotlight_process inputs and write into active inlet */
+ switch(pdp_packet_header(x->x_packet0)->info.image.encoding){
+
+ case PDP_IMAGE_YV12:
+ x->x_packet1 = pdp_packet_clone_rw(x->x_packet0);
+ pdp_queue_add(x, pdp_spotlight_process_yv12, pdp_spotlight_sendpacket, &x->x_queue_id);
+ break;
+
+ default:
+ /* don't know the type, so dont pdp_spotlight_process */
+ break;
+
+ }
+ }
+}
+
+static void pdp_spotlight_input_0(t_pdp_spotlight *x, t_symbol *s, t_floatarg f)
+{
+ /* if this is a register_ro message or register_rw message, register with packet factory */
+
+ if (s== gensym("register_rw"))
+ {
+ x->x_dropped = pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("image/YCrCb/*") );
+ }
+
+ if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped))
+ {
+ /* add the process method and callback to the process queue */
+ pdp_spotlight_process(x);
+
+ }
+}
+
+static void pdp_spotlight_free(t_pdp_spotlight *x)
+{
+ int i;
+
+ pdp_queue_finish(x->x_queue_id);
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+t_class *pdp_spotlight_class;
+
+void *pdp_spotlight_new(void)
+{
+ int i;
+
+ t_pdp_spotlight *x = (t_pdp_spotlight *)pd_new(pdp_spotlight_class);
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cx"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("cy"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ssize"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("r"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("g"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("b"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("strength"));
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ x->x_packet0 = -1;
+ x->x_packet1 = -1;
+ x->x_queue_id = -1;
+
+ x->x_cx = 70;
+ x->x_cy = 70;
+ x->x_ssize = 50;
+ x->x_colorR = 255;
+ x->x_colorG = 255;
+ x->x_colorB = 255;
+ x->x_strength = 0.5;
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_spotlight_setup(void)
+{
+// post( pdp_spotlight_version );
+ pdp_spotlight_class = class_new(gensym("pdp_spotlight"), (t_newmethod)pdp_spotlight_new,
+ (t_method)pdp_spotlight_free, sizeof(t_pdp_spotlight), 0, A_NULL);
+
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_cx, gensym("cx"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_cy, gensym("cy"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_ssize, gensym("ssize"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_r, gensym("r"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_g, gensym("g"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_b, gensym("b"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_spotlight_class, (t_method)pdp_spotlight_strength, gensym("strength"), A_FLOAT, A_NULL);
+ class_sethelpsymbol( pdp_spotlight_class, gensym("pdp_spotlight.pd") );
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/patches/pdp_cabaret.pd b/patches/pdp_cabaret.pd
new file mode 100644
index 0000000..1f72e30
--- /dev/null
+++ b/patches/pdp_cabaret.pd
@@ -0,0 +1,119 @@
+#N canvas 275 13 818 664 10;
+#X obj 191 19 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 46 91 loop \$1;
+#X obj 47 69 tgl 15 0 empty empty empty 20 8 0 8 -262144 -1 -1 0 1
+;
+#X msg 75 52 open \$1;
+#X obj 74 28 openpanel;
+#X obj 59 11 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X floatatom 239 54 5 0 0 0 - - -;
+#X msg 148 20 stop;
+#X obj 246 23 hsl 300 15 0 1000 0 0 empty empty empty -2 -6 0 8 -262144
+-1 -1 0 1;
+#X obj 180 90 metro 70;
+#X floatatom 264 435 5 0 0 0 - - -;
+#X floatatom 278 457 5 0 0 0 - - -;
+#X floatatom 291 478 5 0 0 0 - - -;
+#X obj 175 122 pdp_yqt;
+#X obj 313 118 pdp_v4l;
+#X obj 322 87 metro 70;
+#X obj 367 53 bng 15 250 50 0 empty empty empty 20 8 0 8 -262144 -1
+-1;
+#X msg 324 54 stop;
+#X msg 409 85 open /dev/video;
+#X obj 547 523 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 547 575 pdp_control;
+#X msg 547 548 thread \$1;
+#X floatatom 547 636 5 0 0 0 - - -;
+#X obj 547 607 route pdp_drop;
+#X text 100 619 author : Yves Degoyon ( ydegoyon@free.fr );
+#X obj 416 394 colorgrid colorgrid1 256 0 256 50 0 50 0 1 1 10 10 481
+420;
+#X obj 680 393 vsl 8 50 0 1 0 0 empty empty empty 0 -8 0 8 -262144
+-1 -1 0 1;
+#X obj 213 200 pdp_shape;
+#X obj 258 243 -;
+#X floatatom 265 266 5 0 0 0 - - -;
+#X text 310 267 Width;
+#X obj 370 241 -;
+#X floatatom 372 265 5 0 0 0 - - -;
+#X text 418 265 Height;
+#X obj 221 555 pdp_xv;
+#X obj 32 452 route press drag release;
+#X msg 54 230 pick \$1 \$2;
+#X msg 53 258 detect \$1 \$2;
+#X msg 167 420 0;
+#X obj 324 321 max;
+#X obj 236 331 +;
+#X obj 234 356 / 2;
+#X obj 279 332 +;
+#X obj 277 357 / 2;
+#X obj 324 356 / 2;
+#X msg 330 196 shape 0;
+#X obj 330 173 loadbang;
+#X obj 165 397 loadbang;
+#X msg 295 542 cursor 1;
+#X text 100 605 pdp_cabaret : specially made for pep';
+#X obj 223 515 pdp_spotlight;
+#X connect 0 0 9 0;
+#X connect 1 0 13 0;
+#X connect 2 0 1 0;
+#X connect 3 0 13 0;
+#X connect 4 0 3 0;
+#X connect 5 0 4 0;
+#X connect 6 0 9 1;
+#X connect 7 0 9 0;
+#X connect 8 0 6 0;
+#X connect 9 0 13 0;
+#X connect 10 0 50 1;
+#X connect 11 0 50 2;
+#X connect 12 0 50 3;
+#X connect 13 0 27 0;
+#X connect 14 0 27 0;
+#X connect 15 0 14 0;
+#X connect 16 0 15 0;
+#X connect 17 0 15 0;
+#X connect 18 0 14 0;
+#X connect 19 0 21 0;
+#X connect 20 0 23 0;
+#X connect 21 0 20 0;
+#X connect 23 0 22 0;
+#X connect 25 0 50 4;
+#X connect 25 1 50 5;
+#X connect 25 2 50 6;
+#X connect 26 0 50 7;
+#X connect 27 0 48 0;
+#X connect 27 0 50 0;
+#X connect 27 1 28 1;
+#X connect 27 1 40 0;
+#X connect 27 2 31 1;
+#X connect 27 2 42 0;
+#X connect 27 3 28 0;
+#X connect 27 3 40 1;
+#X connect 27 4 31 0;
+#X connect 27 4 42 1;
+#X connect 28 0 29 0;
+#X connect 29 0 39 0;
+#X connect 31 0 32 0;
+#X connect 32 0 39 1;
+#X connect 34 0 35 0;
+#X connect 35 0 36 0;
+#X connect 35 0 38 0;
+#X connect 35 2 37 0;
+#X connect 36 0 27 0;
+#X connect 37 0 27 0;
+#X connect 38 0 12 0;
+#X connect 39 0 44 0;
+#X connect 40 0 41 0;
+#X connect 41 0 10 0;
+#X connect 42 0 43 0;
+#X connect 43 0 11 0;
+#X connect 44 0 12 0;
+#X connect 45 0 27 0;
+#X connect 46 0 45 0;
+#X connect 47 0 38 0;
+#X connect 48 0 34 0;
+#X connect 50 0 34 0;