blob: f6c49d0ffe89cfc5d2ffb120b59cb532a64e1a9e [file] [log] [blame]
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001/* $Id$ */
2/*
3 * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjmedia/vid_stream.h>
20#include <pjmedia/errno.h>
21#include <pjmedia/rtp.h>
22#include <pjmedia/rtcp.h>
23#include <pjmedia/jbuf.h>
24#include <pjmedia/stream_common.h>
25#include <pj/array.h>
26#include <pj/assert.h>
27#include <pj/ctype.h>
28#include <pj/compat/socket.h>
29#include <pj/errno.h>
30#include <pj/ioqueue.h>
31#include <pj/log.h>
32#include <pj/os.h>
33#include <pj/pool.h>
34#include <pj/rand.h>
35#include <pj/sock_select.h>
36#include <pj/string.h> /* memcpy() */
37
38
39#define THIS_FILE "vid_stream.c"
40#define ERRLEVEL 1
41#define LOGERR_(expr) stream_perror expr
42#define TRC_(expr) PJ_LOG(5,expr)
43
44/* Tracing jitter buffer operations in a stream session to a CSV file.
45 * The trace will contain JB operation timestamp, frame info, RTP info, and
46 * the JB state right after the operation.
47 */
48#define TRACE_JB 0 /* Enable/disable trace. */
49#define TRACE_JB_PATH_PREFIX "" /* Optional path/prefix
50 for the CSV filename. */
51#if TRACE_JB
52# include <pj/file_io.h>
53# define TRACE_JB_INVALID_FD ((pj_oshandle_t)-1)
54# define TRACE_JB_OPENED(s) (s->trace_jb_fd != TRACE_JB_INVALID_FD)
55#endif
56
57
58
59/**
60 * Media channel.
61 */
62typedef struct pjmedia_vid_channel
63{
64 pjmedia_vid_stream *stream; /**< Parent stream. */
65 pjmedia_dir dir; /**< Channel direction. */
66 pjmedia_port port; /**< Port interface. */
67 unsigned pt; /**< Payload type. */
68 pj_bool_t paused; /**< Paused?. */
69 void *buf; /**< Output buffer. */
70 unsigned buf_size; /**< Size of output buffer. */
71 unsigned buf_len; /**< Length of data in buffer. */
72 pjmedia_rtp_session rtp; /**< RTP session. */
73} pjmedia_vid_channel;
74
75
76/**
77 * This structure describes media stream.
78 * A media stream is bidirectional media transmission between two endpoints.
79 * It consists of two channels, i.e. encoding and decoding channels.
80 * A media stream corresponds to a single "m=" line in a SDP session
81 * description.
82 */
83struct pjmedia_vid_stream
84{
85 pjmedia_endpt *endpt; /**< Media endpoint. */
86 pjmedia_vid_codec_mgr *codec_mgr; /**< Codec manager. */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +000087 pjmedia_vid_stream_info info; /**< Stream info. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +000088
89 pjmedia_vid_channel *enc; /**< Encoding channel. */
90 pjmedia_vid_channel *dec; /**< Decoding channel. */
91
92 pjmedia_dir dir; /**< Stream direction. */
93 void *user_data; /**< User data. */
94 pj_str_t name; /**< Stream name */
95 pj_str_t cname; /**< SDES CNAME */
96
97 pjmedia_transport *transport; /**< Stream transport. */
98
99 pj_mutex_t *jb_mutex;
100 pjmedia_jbuf *jb; /**< Jitter buffer. */
101 char jb_last_frm; /**< Last frame type from jb */
102 unsigned jb_last_frm_cnt;/**< Last JB frame type counter*/
103
104 pjmedia_rtcp_session rtcp; /**< RTCP for incoming RTP. */
105 pj_uint32_t rtcp_last_tx; /**< RTCP tx time in timestamp */
106 pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */
107 pj_bool_t initial_rr; /**< Initial RTCP RR sent */
108
109 unsigned frame_size; /**< Size of encoded base frame.*/
110 unsigned frame_ts_len; /**< Frame length in timestamp. */
111
112#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
113 pj_uint32_t rtcp_xr_last_tx; /**< RTCP XR tx time
114 in timestamp. */
115 pj_uint32_t rtcp_xr_interval; /**< Interval, in timestamp. */
116 pj_sockaddr rtcp_xr_dest; /**< Additional remote RTCP XR
117 dest. If sin_family is
118 zero, it will be ignored*/
119 unsigned rtcp_xr_dest_len; /**< Length of RTCP XR dest
120 address */
121#endif
122
123#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
124 pj_bool_t use_ka; /**< Stream keep-alive with non-
125 codec-VAD mechanism is
126 enabled? */
127 pj_timestamp last_frm_ts_sent; /**< Timestamp of last sending
128 packet */
129#endif
130
131#if TRACE_JB
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000132 pj_oshandle_t trace_jb_fd; /**< Jitter tracing file handle.*/
133 char *trace_jb_buf; /**< Jitter tracing buffer. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000134#endif
135
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000136 pjmedia_vid_codec *codec; /**< Codec instance being used. */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000137};
138
139
140/*
141 * Print error.
142 */
143static void stream_perror(const char *sender, const char *title,
144 pj_status_t status)
145{
146 char errmsg[PJ_ERR_MSG_SIZE];
147
148 pj_strerror(status, errmsg, sizeof(errmsg));
149 PJ_LOG(4,(sender, "%s: %s [err:%d]", title, errmsg, status));
150}
151
152
153#if TRACE_JB
154
155PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len)
156{
157 pj_time_val now;
158 pj_parsed_time ptime;
159 char *p = *buf;
160
161 if (len < 14)
162 return -1;
163
164 pj_gettimeofday(&now);
165 pj_time_decode(&now, &ptime);
166 p += pj_utoa_pad(ptime.hour, p, 2, '0');
167 *p++ = ':';
168 p += pj_utoa_pad(ptime.min, p, 2, '0');
169 *p++ = ':';
170 p += pj_utoa_pad(ptime.sec, p, 2, '0');
171 *p++ = '.';
172 p += pj_utoa_pad(ptime.msec, p, 3, '0');
173 *p++ = ',';
174
175 *buf = p;
176
177 return 0;
178}
179
180PJ_INLINE(int) trace_jb_print_state(pjmedia_vid_stream *stream,
181 char **buf, pj_ssize_t len)
182{
183 char *p = *buf;
184 char *endp = *buf + len;
185 pjmedia_jb_state state;
186
187 pjmedia_jbuf_get_state(stream->jb, &state);
188
189 len = pj_ansi_snprintf(p, endp-p, "%d, %d, %d",
190 state.size, state.burst, state.prefetch);
191 if ((len < 0) || (len >= endp-p))
192 return -1;
193
194 p += len;
195 *buf = p;
196 return 0;
197}
198
199static void trace_jb_get(pjmedia_vid_stream *stream, pjmedia_jb_frame_type ft,
200 pj_size_t fsize)
201{
202 char *p = stream->trace_jb_buf;
203 char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
204 pj_ssize_t len = 0;
205 const char* ft_st;
206
207 if (!TRACE_JB_OPENED(stream))
208 return;
209
210 /* Print timestamp. */
211 if (trace_jb_print_timestamp(&p, endp-p))
212 goto on_insuff_buffer;
213
214 /* Print frame type and size */
215 switch(ft) {
216 case PJMEDIA_JB_MISSING_FRAME:
217 ft_st = "missing";
218 break;
219 case PJMEDIA_JB_NORMAL_FRAME:
220 ft_st = "normal";
221 break;
222 case PJMEDIA_JB_ZERO_PREFETCH_FRAME:
223 ft_st = "prefetch";
224 break;
225 case PJMEDIA_JB_ZERO_EMPTY_FRAME:
226 ft_st = "empty";
227 break;
228 default:
229 ft_st = "unknown";
230 break;
231 }
232
233 /* Print operation, size, frame count, frame type */
234 len = pj_ansi_snprintf(p, endp-p, "GET,%d,1,%s,,,,", fsize, ft_st);
235 if ((len < 0) || (len >= endp-p))
236 goto on_insuff_buffer;
237 p += len;
238
239 /* Print JB state */
240 if (trace_jb_print_state(stream, &p, endp-p))
241 goto on_insuff_buffer;
242
243 /* Print end of line */
244 if (endp-p < 2)
245 goto on_insuff_buffer;
246 *p++ = '\n';
247
248 /* Write and flush */
249 len = p - stream->trace_jb_buf;
250 pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
251 pj_file_flush(stream->trace_jb_fd);
252 return;
253
254on_insuff_buffer:
255 pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
256}
257
258static void trace_jb_put(pjmedia_vid_stream *stream,
259 const pjmedia_rtp_hdr *hdr,
260 unsigned payloadlen, unsigned frame_cnt)
261{
262 char *p = stream->trace_jb_buf;
263 char *endp = stream->trace_jb_buf + PJ_LOG_MAX_SIZE;
264 pj_ssize_t len = 0;
265
266 if (!TRACE_JB_OPENED(stream))
267 return;
268
269 /* Print timestamp. */
270 if (trace_jb_print_timestamp(&p, endp-p))
271 goto on_insuff_buffer;
272
273 /* Print operation, size, frame count, RTP info */
274 len = pj_ansi_snprintf(p, endp-p,
275 "PUT,%d,%d,,%d,%d,%d,",
276 payloadlen, frame_cnt,
277 pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), hdr->m);
278 if ((len < 0) || (len >= endp-p))
279 goto on_insuff_buffer;
280 p += len;
281
282 /* Print JB state */
283 if (trace_jb_print_state(stream, &p, endp-p))
284 goto on_insuff_buffer;
285
286 /* Print end of line */
287 if (endp-p < 2)
288 goto on_insuff_buffer;
289 *p++ = '\n';
290
291 /* Write and flush */
292 len = p - stream->trace_jb_buf;
293 pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
294 pj_file_flush(stream->trace_jb_fd);
295 return;
296
297on_insuff_buffer:
298 pj_assert(!"Trace buffer too small, check PJ_LOG_MAX_SIZE!");
299}
300
301#endif /* TRACE_JB */
302
303
304#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
305/*
306 * Send keep-alive packet using non-codec frame.
307 */
308static void send_keep_alive_packet(pjmedia_vid_stream *stream)
309{
310#if PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_EMPTY_RTP
311
312 /* Keep-alive packet is empty RTP */
313 pj_status_t status;
314 void *pkt;
315 int pkt_len;
316
317 TRC_((channel->port.info.name.ptr,
318 "Sending keep-alive (RTCP and empty RTP)"));
319
320 /* Send RTP */
321 status = pjmedia_rtp_encode_rtp( &stream->enc->rtp,
322 stream->enc->pt, 0,
323 1,
324 0,
325 (const void**)&pkt,
326 &pkt_len);
327 pj_assert(status == PJ_SUCCESS);
328
329 pj_memcpy(stream->enc->buf, pkt, pkt_len);
330 pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
331 pkt_len);
332
333 /* Send RTCP */
334 pjmedia_rtcp_build_rtcp(&stream->rtcp, &pkt, &pkt_len);
335 pjmedia_transport_send_rtcp(stream->transport, pkt, pkt_len);
336
337#elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
338
339 /* Keep-alive packet is defined in PJMEDIA_STREAM_KA_USER_PKT */
340 int pkt_len;
341 const pj_str_t str_ka = PJMEDIA_STREAM_KA_USER_PKT;
342
343 TRC_((channel->port.info.name.ptr,
344 "Sending keep-alive (custom RTP/RTCP packets)"));
345
346 /* Send to RTP port */
347 pj_memcpy(stream->enc->buf, str_ka.ptr, str_ka.slen);
348 pkt_len = str_ka.slen;
349 pjmedia_transport_send_rtp(stream->transport, stream->enc->buf,
350 pkt_len);
351
352 /* Send to RTCP port */
353 pjmedia_transport_send_rtcp(stream->transport, stream->enc->buf,
354 pkt_len);
355
356#else
357
358 PJ_UNUSED_ARG(stream);
359
360#endif
361}
362#endif /* defined(PJMEDIA_STREAM_ENABLE_KA) */
363
364
365/**
366 * check_tx_rtcp()
367 *
368 * This function is can be called by either put_frame() or get_frame(),
369 * to transmit periodic RTCP SR/RR report.
370 */
371static void check_tx_rtcp(pjmedia_vid_stream *stream, pj_uint32_t timestamp)
372{
373 /* Note that timestamp may represent local or remote timestamp,
374 * depending on whether this function is called from put_frame()
375 * or get_frame().
376 */
377
378
379 if (stream->rtcp_last_tx == 0) {
380
381 stream->rtcp_last_tx = timestamp;
382
383 } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
384
385 void *rtcp_pkt;
386 int len;
387
388 pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
389
390 pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
391
392 stream->rtcp_last_tx = timestamp;
393 }
394
395#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
396 if (stream->rtcp.xr_enabled) {
397
398 if (stream->rtcp_xr_last_tx == 0) {
399
400 stream->rtcp_xr_last_tx = timestamp;
401
402 } else if (timestamp - stream->rtcp_xr_last_tx >=
403 stream->rtcp_xr_interval)
404 {
405 int i;
406 pjmedia_jb_state jb_state;
407 void *rtcp_pkt;
408 int len;
409
410 /* Update RTCP XR with current JB states */
411 pjmedia_jbuf_get_state(stream->jb, &jb_state);
412
413 i = jb_state.avg_delay;
414 pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
415 PJMEDIA_RTCP_XR_INFO_JB_NOM,
416 i);
417
418 i = jb_state.max_delay;
419 pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
420 PJMEDIA_RTCP_XR_INFO_JB_MAX,
421 i);
422
423 /* Build RTCP XR packet */
424 pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
425 &rtcp_pkt, &len);
426
427 /* Send the RTCP XR to remote address */
428 pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
429
430 /* Send the RTCP XR to third-party destination if specified */
431 if (stream->rtcp_xr_dest_len) {
432 pjmedia_transport_send_rtcp2(stream->transport,
433 &stream->rtcp_xr_dest,
434 stream->rtcp_xr_dest_len,
435 rtcp_pkt, len);
436 }
437
438 /* Update last tx RTCP XR */
439 stream->rtcp_xr_last_tx = timestamp;
440 }
441 }
442#endif
443}
444
445/* Build RTCP SDES packet */
446static unsigned create_rtcp_sdes(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
447 unsigned max_len)
448{
449 pjmedia_rtcp_common hdr;
450 pj_uint8_t *p = pkt;
451
452 /* SDES header */
453 hdr.version = 2;
454 hdr.p = 0;
455 hdr.count = 1;
456 hdr.pt = 202;
457 hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
458 if (max_len < (hdr.length << 2)) {
459 pj_assert(!"Not enough buffer for SDES packet");
460 return 0;
461 }
462 hdr.length = pj_htons((pj_uint16_t)hdr.length);
463 hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
464 pj_memcpy(p, &hdr, sizeof(hdr));
465 p += sizeof(hdr);
466
467 /* CNAME item */
468 *p++ = 1;
469 *p++ = (pj_uint8_t)stream->cname.slen;
470 pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
471 p += stream->cname.slen;
472
473 /* END */
474 *p++ = '\0';
475 *p++ = '\0';
476
477 /* Pad to 32bit */
478 while ((p-pkt) % 4)
479 *p++ = '\0';
480
481 return (p - pkt);
482}
483
484/* Build RTCP BYE packet */
485static unsigned create_rtcp_bye(pjmedia_vid_stream *stream, pj_uint8_t *pkt,
486 unsigned max_len)
487{
488 pjmedia_rtcp_common hdr;
489
490 /* BYE header */
491 hdr.version = 2;
492 hdr.p = 0;
493 hdr.count = 1;
494 hdr.pt = 203;
495 hdr.length = 1;
496 if (max_len < (hdr.length << 2)) {
497 pj_assert(!"Not enough buffer for SDES packet");
498 return 0;
499 }
500 hdr.length = pj_htons((pj_uint16_t)hdr.length);
501 hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
502 pj_memcpy(pkt, &hdr, sizeof(hdr));
503
504 return sizeof(hdr);
505}
506
507
508#if 0
509static void dump_bin(const char *buf, unsigned len)
510{
511 unsigned i;
512
513 PJ_LOG(3,(THIS_FILE, "begin dump"));
514 for (i=0; i<len; ++i) {
515 int j;
516 char bits[9];
517 unsigned val = buf[i] & 0xFF;
518
519 bits[8] = '\0';
520 for (j=0; j<8; ++j) {
521 if (val & (1 << (7-j)))
522 bits[j] = '1';
523 else
524 bits[j] = '0';
525 }
526
527 PJ_LOG(3,(THIS_FILE, "%2d %s [%d]", i, bits, val));
528 }
529 PJ_LOG(3,(THIS_FILE, "end dump"));
530}
531#endif
532
533
534/*
535 * This callback is called by stream transport on receipt of packets
536 * in the RTP socket.
537 */
538static void on_rx_rtp( void *data,
539 void *pkt,
540 pj_ssize_t bytes_read)
541
542{
543 pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
544 pjmedia_vid_channel *channel = stream->dec;
545 const pjmedia_rtp_hdr *hdr;
546 const void *payload;
547 unsigned payloadlen;
548 pjmedia_rtp_status seq_st;
549 pj_status_t status;
550 pj_bool_t pkt_discarded = PJ_FALSE;
551
552 /* Check for errors */
553 if (bytes_read < 0) {
554 LOGERR_((channel->port.info.name.ptr, "RTP recv() error", -bytes_read));
555 return;
556 }
557
558 /* Ignore keep-alive packets */
559 if (bytes_read < (pj_ssize_t) sizeof(pjmedia_rtp_hdr))
560 return;
561
562 /* Update RTP and RTCP session. */
563 status = pjmedia_rtp_decode_rtp(&channel->rtp, pkt, bytes_read,
564 &hdr, &payload, &payloadlen);
565 if (status != PJ_SUCCESS) {
566 LOGERR_((channel->port.info.name.ptr, "RTP decode error", status));
567 stream->rtcp.stat.rx.discard++;
568 return;
569 }
570
571 /* Ignore the packet if decoder is paused */
572 if (channel->paused)
573 goto on_return;
574
575 /* Update RTP session (also checks if RTP session can accept
576 * the incoming packet.
577 */
578 pjmedia_rtp_session_update2(&channel->rtp, hdr, &seq_st, PJ_TRUE);
579 if (seq_st.status.value) {
580 TRC_ ((channel->port.info.name.ptr,
581 "RTP status: badpt=%d, badssrc=%d, dup=%d, "
582 "outorder=%d, probation=%d, restart=%d",
583 seq_st.status.flag.badpt,
584 seq_st.status.flag.badssrc,
585 seq_st.status.flag.dup,
586 seq_st.status.flag.outorder,
587 seq_st.status.flag.probation,
588 seq_st.status.flag.restart));
589
590 if (seq_st.status.flag.badpt) {
591 PJ_LOG(4,(channel->port.info.name.ptr,
592 "Bad RTP pt %d (expecting %d)",
593 hdr->pt, channel->rtp.out_pt));
594 }
595
596 if (seq_st.status.flag.badssrc) {
597 PJ_LOG(4,(channel->port.info.name.ptr,
598 "Changed RTP peer SSRC %d (previously %d)",
599 channel->rtp.peer_ssrc, stream->rtcp.peer_ssrc));
600 stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
601 }
602
603
604 }
605
606 /* Skip bad RTP packet */
607 if (seq_st.status.flag.bad) {
608 pkt_discarded = PJ_TRUE;
609 goto on_return;
610 }
611
612 /* Ignore if payloadlen is zero */
613 if (payloadlen == 0) {
614 pkt_discarded = PJ_TRUE;
615 goto on_return;
616 }
617
618
619 /* Put "good" packet to jitter buffer, or reset the jitter buffer
620 * when RTP session is restarted.
621 */
622 pj_mutex_lock( stream->jb_mutex );
623 if (seq_st.status.flag.restart) {
624 status = pjmedia_jbuf_reset(stream->jb);
625 PJ_LOG(4,(channel->port.info.name.ptr, "Jitter buffer reset"));
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000626 } else {
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000627 /* Just put the payload into jitter buffer */
628 pjmedia_jbuf_put_frame3(stream->jb, payload, payloadlen, 0,
629 pj_ntohs(hdr->seq), pj_ntohl(hdr->ts), NULL);
630
631#if TRACE_JB
632 trace_jb_put(stream, hdr, payloadlen, count);
633#endif
634
635 }
636 pj_mutex_unlock( stream->jb_mutex );
637
638
639 /* Check if now is the time to transmit RTCP SR/RR report.
640 * We only do this when stream direction is "decoding only",
641 * because otherwise check_tx_rtcp() will be handled by put_frame()
642 */
643 if (stream->dir == PJMEDIA_DIR_DECODING) {
644 check_tx_rtcp(stream, pj_ntohl(hdr->ts));
645 }
646
647 if (status != 0) {
648 LOGERR_((channel->port.info.name.ptr, "Jitter buffer put() error",
649 status));
650 pkt_discarded = PJ_TRUE;
651 goto on_return;
652 }
653
654on_return:
655 /* Update RTCP session */
656 if (stream->rtcp.peer_ssrc == 0)
657 stream->rtcp.peer_ssrc = channel->rtp.peer_ssrc;
658
659 pjmedia_rtcp_rx_rtp2(&stream->rtcp, pj_ntohs(hdr->seq),
660 pj_ntohl(hdr->ts), payloadlen, pkt_discarded);
661
662 /* Send RTCP RR and SDES after we receive some RTP packets */
663 if (stream->rtcp.received >= 10 && !stream->initial_rr) {
664 void *sr_rr_pkt;
665 pj_uint8_t *pkt;
666 int len;
667
668 /* Build RR or SR */
669 pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
670 pkt = (pj_uint8_t*) stream->enc->buf;
671 pj_memcpy(pkt, sr_rr_pkt, len);
672 pkt += len;
673
674 /* Append SDES */
675 len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt,
676 stream->enc->buf_size - len);
677 if (len > 0) {
678 pkt += len;
679 len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->buf);
680 pjmedia_transport_send_rtcp(stream->transport,
681 stream->enc->buf, len);
682 }
683
684 stream->initial_rr = PJ_TRUE;
685 }
686}
687
688
689/*
690 * This callback is called by stream transport on receipt of packets
691 * in the RTCP socket.
692 */
693static void on_rx_rtcp( void *data,
694 void *pkt,
695 pj_ssize_t bytes_read)
696{
697 pjmedia_vid_stream *stream = (pjmedia_vid_stream*) data;
698
699 /* Check for errors */
700 if (bytes_read < 0) {
701 LOGERR_((stream->cname.ptr, "RTCP recv() error",
702 -bytes_read));
703 return;
704 }
705
706 pjmedia_rtcp_rx_rtcp(&stream->rtcp, pkt, bytes_read);
707}
708
709static pj_status_t put_frame(pjmedia_port *port,
710 pjmedia_frame *frame)
711{
712 pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
713 pjmedia_vid_channel *channel = stream->enc;
714 pj_status_t status = 0;
715 pjmedia_frame frame_out;
716 unsigned rtp_ts_len;
717 void *rtphdr;
718 int rtphdrlen;
719 unsigned processed = 0;
720
721
722#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA != 0
723 /* If the interval since last sending packet is greater than
724 * PJMEDIA_STREAM_KA_INTERVAL, send keep-alive packet.
725 */
726 if (stream->use_ka)
727 {
728 pj_uint32_t dtx_duration;
729
730 dtx_duration = pj_timestamp_diff32(&stream->last_frm_ts_sent,
731 &frame->timestamp);
732 if (dtx_duration >
733 PJMEDIA_STREAM_KA_INTERVAL * channel->port.info.clock_rate)
734 {
735 send_keep_alive_packet(stream);
736 stream->last_frm_ts_sent = frame->timestamp;
737 }
738 }
739#endif
740
741 /* Don't do anything if stream is paused */
742 if (channel->paused) {
743 return PJ_SUCCESS;
744 }
745
746 /* Get frame length in timestamp unit */
747 rtp_ts_len = stream->frame_ts_len;
748
749 /* Init frame_out buffer. */
750 frame_out.buf = ((char*)channel->buf) + sizeof(pjmedia_rtp_hdr);
751 frame_out.size = 0;
752
753 /* Encode! */
754 status = (*stream->codec->op->encode)(stream->codec, frame,
755 channel->buf_size -
756 sizeof(pjmedia_rtp_hdr),
757 &frame_out);
758 if (status != PJ_SUCCESS) {
759 LOGERR_((channel->port.info.name.ptr,
760 "Codec encode() error", status));
761 return status;
762 }
763
764
765 while (processed < frame_out.size) {
766 pj_uint8_t *payload, *rtp_pkt;
767 pj_size_t payload_len;
768
769 /* Generate RTP payload */
770 status = (*stream->codec->op->packetize)(
771 stream->codec,
772 (pj_uint8_t*)frame_out.buf,
773 frame_out.size,
774 &processed,
775 &payload, &payload_len);
776 if (status != PJ_SUCCESS) {
777 LOGERR_((channel->port.info.name.ptr,
778 "Codec pack() error", status));
779 return status;
780 }
781
782 /* Encapsulate. */
783 status = pjmedia_rtp_encode_rtp( &channel->rtp,
784 channel->pt,
785 (processed==frame_out.size?1:0),
786 payload_len,
787 rtp_ts_len,
788 (const void**)&rtphdr,
789 &rtphdrlen);
790
791 if (status != PJ_SUCCESS) {
792 LOGERR_((channel->port.info.name.ptr,
793 "RTP encode_rtp() error", status));
794 return status;
795 }
796
797 /* Next packets use same timestamp */
798 rtp_ts_len = 0;
799
800 rtp_pkt = payload - sizeof(pjmedia_rtp_hdr);
801
802 /* Copy RTP header to the beginning of packet */
803 pj_memcpy(rtp_pkt, rtphdr, sizeof(pjmedia_rtp_hdr));
804
805 /* Send the RTP packet to the transport. */
806 pjmedia_transport_send_rtp(stream->transport, rtp_pkt,
807 payload_len + sizeof(pjmedia_rtp_hdr));
808 }
809
810 /* Check if now is the time to transmit RTCP SR/RR report.
811 * We only do this when stream direction is not "decoding only", because
812 * when it is, check_tx_rtcp() will be handled by get_frame().
813 */
814 if (stream->dir != PJMEDIA_DIR_DECODING) {
815 check_tx_rtcp(stream, pj_ntohl(channel->rtp.out_hdr.ts));
816 }
817
818 /* Do nothing if we have nothing to transmit */
819 if (frame_out.size == 0) {
820 return PJ_SUCCESS;
821 }
822
823 /* Update stat */
824 pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
825 stream->rtcp.stat.rtp_tx_last_ts = pj_ntohl(stream->enc->rtp.out_hdr.ts);
826 stream->rtcp.stat.rtp_tx_last_seq = pj_ntohs(stream->enc->rtp.out_hdr.seq);
827
828#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
829 /* Update timestamp of last sending packet. */
830 stream->last_frm_ts_sent = frame->timestamp;
831#endif
832
833 return PJ_SUCCESS;
834}
835
836
837static pj_status_t get_frame(pjmedia_port *port,
838 pjmedia_frame *frame)
839{
840 pjmedia_vid_stream *stream = (pjmedia_vid_stream*) port->port_data.pdata;
841 pjmedia_vid_channel *channel = stream->dec;
842 pjmedia_frame frame_in;
843 pj_status_t status;
844
845 /* Return no frame is channel is paused */
846 if (channel->paused) {
847 frame->type = PJMEDIA_FRAME_TYPE_NONE;
848 return PJ_SUCCESS;
849 }
850
851 /* Repeat get payload from the jitter buffer until all payloads with same
852 * timestamp are collected (a complete frame unpacketized).
853 */
854 {
855 pj_uint8_t *p, *data;
856 char ptype;
857 pj_size_t psize, data_len;
858 pj_uint32_t ts, last_ts;
859 pj_bool_t got_frame;
860 unsigned i;
861
862 channel->buf_len = 0;
863 last_ts = 0;
864 got_frame = PJ_FALSE;
865
866 /* Lock jitter buffer mutex first */
867 pj_mutex_lock( stream->jb_mutex );
868
869 for (i=0; ; ++i) {
870 /* Get frame from jitter buffer. */
871 pjmedia_jbuf_peek_frame(stream->jb, i, &p, &psize, &ptype,
872 NULL, &ts);
873 if (ptype == PJMEDIA_JB_NORMAL_FRAME) {
874 if (last_ts == 0)
875 last_ts = ts;
876
877 if (ts != last_ts) {
878 got_frame = PJ_TRUE;
879 pjmedia_jbuf_remove_frame(stream->jb, i);
880 break;
881 }
882
883 data = (pj_uint8_t*)channel->buf + channel->buf_len;
884 data_len = channel->buf_size - channel->buf_len;
885 status = (*stream->codec->op->unpacketize)(stream->codec,
886 p, psize,
887 data, &data_len);
888 channel->buf_len += data_len;
889 } else if (ptype == PJMEDIA_JB_ZERO_EMPTY_FRAME) {
890 /* No more packet in the jitter buffer */
891 break;
892 }
893 }
894
895 /* Unlock jitter buffer mutex. */
896 pj_mutex_unlock( stream->jb_mutex );
897
898 if (!got_frame) {
899 frame->type = PJMEDIA_FRAME_TYPE_NONE;
900 frame->size = 0;
901 return PJ_SUCCESS;
902 }
903 }
904
905 /* Decode */
906 frame_in.buf = channel->buf;
907 frame_in.size = channel->buf_len;
908 frame_in.bit_info = 0;
909 frame_in.type = PJMEDIA_FRAME_TYPE_VIDEO;
910
911 status = stream->codec->op->decode(stream->codec, &frame_in,
912 frame->size, frame);
913 if (status != PJ_SUCCESS) {
914 LOGERR_((port->info.name.ptr, "codec decode() error",
915 status));
916 frame->type = PJMEDIA_FRAME_TYPE_NONE;
917 frame->size = 0;
918 }
919
920 /* Check if the decoder format is changed */
921 if (frame->bit_info & PJMEDIA_VID_CODEC_EVENT_FMT_CHANGED) {
922 /* Update param from codec */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000923 stream->codec->op->get_param(stream->codec, stream->info.codec_param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000924
925 /* Update decoding channel port info */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000926 pjmedia_format_copy(&stream->dec->port.info.fmt,
927 &stream->info.codec_param->dec_fmt);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000928 }
929
930 return PJ_SUCCESS;
931}
932
933
934/*
935 * Create media channel.
936 */
937static pj_status_t create_channel( pj_pool_t *pool,
938 pjmedia_vid_stream *stream,
939 pjmedia_dir dir,
940 unsigned pt,
941 const pjmedia_vid_stream_info *info,
942 pjmedia_vid_channel **p_channel)
943{
944 enum { M = 32 };
945 pjmedia_vid_channel *channel;
946 pj_status_t status;
947 unsigned min_out_pkt_size;
948 pj_str_t name;
949 const char *type_name;
950 pjmedia_format *fmt;
951
952 pj_assert(info->type == PJMEDIA_TYPE_VIDEO);
953 pj_assert(dir == PJMEDIA_DIR_DECODING || dir == PJMEDIA_DIR_ENCODING);
954
955 /* Allocate memory for channel descriptor */
956 channel = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_channel);
957 PJ_ASSERT_RETURN(channel != NULL, PJ_ENOMEM);
958
959 /* Init vars */
960 if (dir==PJMEDIA_DIR_DECODING) {
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000961 type_name = "vstdec";
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000962 fmt = &info->codec_param->dec_fmt;
963 } else {
Nanang Izzuddin235e1b42011-02-28 18:59:47 +0000964 type_name = "vstenc";
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +0000965 fmt = &info->codec_param->enc_fmt;
966 }
967
968 name.ptr = (char*) pj_pool_alloc(pool, M);
969 name.slen = pj_ansi_snprintf(name.ptr, M, "%s%p", type_name, stream);
970
971 /* Init channel info. */
972 channel->stream = stream;
973 channel->dir = dir;
974 channel->paused = 1;
975 channel->pt = pt;
976
977 /* Allocate buffer for outgoing packet. */
978 channel->buf_size = sizeof(pjmedia_rtp_hdr) + stream->frame_size;
979
980 /* It should big enough to hold (minimally) RTCP SR with an SDES. */
981 min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
982 sizeof(pjmedia_rtcp_common) +
983 (4 + stream->cname.slen) +
984 32;
985
986 if (channel->buf_size < min_out_pkt_size)
987 channel->buf_size = min_out_pkt_size;
988
989 channel->buf = pj_pool_alloc(pool, channel->buf_size);
990 PJ_ASSERT_RETURN(channel->buf != NULL, PJ_ENOMEM);
991
992 /* Create RTP and RTCP sessions: */
993 if (info->rtp_seq_ts_set == 0) {
994 status = pjmedia_rtp_session_init(&channel->rtp, pt, info->ssrc);
995 } else {
996 pjmedia_rtp_session_setting settings;
997
998 settings.flags = (pj_uint8_t)((info->rtp_seq_ts_set << 2) | 3);
999 settings.default_pt = pt;
1000 settings.sender_ssrc = info->ssrc;
1001 settings.seq = info->rtp_seq;
1002 settings.ts = info->rtp_ts;
1003 status = pjmedia_rtp_session_init2(&channel->rtp, settings);
1004 }
1005 if (status != PJ_SUCCESS)
1006 return status;
1007
1008 /* Init port. */
1009 pjmedia_port_info_init2(&channel->port.info, &name,
1010 PJMEDIA_PORT_SIGNATURE('V', 'C', 'H', 'N'),
1011 dir, fmt);
1012 if (dir == PJMEDIA_DIR_DECODING) {
1013 channel->port.get_frame = &get_frame;
1014 } else {
1015 channel->port.put_frame = &put_frame;
1016 }
1017
1018 /* Init port. */
1019 channel->port.port_data.pdata = stream;
1020
1021 /* Done. */
1022 *p_channel = channel;
1023 return PJ_SUCCESS;
1024}
1025
1026
1027/*
1028 * Create stream.
1029 */
1030PJ_DEF(pj_status_t) pjmedia_vid_stream_create(
1031 pjmedia_endpt *endpt,
1032 pj_pool_t *pool,
1033 const pjmedia_vid_stream_info *info,
1034 pjmedia_transport *tp,
1035 void *user_data,
1036 pjmedia_vid_stream **p_stream)
1037{
1038 enum { M = 32 };
1039 pjmedia_vid_stream *stream;
1040 unsigned jb_init, jb_max, jb_min_pre, jb_max_pre, len;
1041 int frm_ptime, chunks_per_frm;
1042 pjmedia_video_format_detail *vfd_enc;
1043 char *p;
1044 pj_status_t status;
1045
1046 /* Allocate stream */
1047 stream = PJ_POOL_ZALLOC_T(pool, pjmedia_vid_stream);
1048 PJ_ASSERT_RETURN(stream != NULL, PJ_ENOMEM);
1049
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001050 /* Copy stream info */
1051 pj_memcpy(&stream->info, info, sizeof(*info));
1052
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001053 /* Get codec manager */
1054 stream->codec_mgr = pjmedia_vid_codec_mgr_instance();
1055 PJ_ASSERT_RETURN(stream->codec_mgr, PJMEDIA_CODEC_EFAILED);
1056
1057 /* Init stream/port name */
1058 stream->name.ptr = (char*) pj_pool_alloc(pool, M);
1059 stream->name.slen = pj_ansi_snprintf(stream->name.ptr, M,
1060 "vstrm%p", stream);
1061
1062 /* Create and initialize codec: */
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001063 status = pjmedia_vid_codec_mgr_alloc_codec(stream->codec_mgr,
1064 &info->codec_info,
1065 &stream->codec);
1066 if (status != PJ_SUCCESS)
1067 return status;
1068
1069
1070 /* Get codec param: */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001071 if (info->codec_param) {
1072 stream->info.codec_param = pjmedia_vid_codec_param_clone(
1073 pool,
1074 info->codec_param);
1075 } else {
1076 pjmedia_vid_codec_param def_param;
1077
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001078 status = pjmedia_vid_codec_mgr_get_default_param(stream->codec_mgr,
1079 &info->codec_info,
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001080 &def_param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001081 if (status != PJ_SUCCESS)
1082 return status;
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001083 stream->info.codec_param = pjmedia_vid_codec_param_clone(
1084 pool,
1085 &def_param);
1086 pj_assert(stream->info.codec_param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001087 }
1088
1089 vfd_enc = pjmedia_format_get_video_format_detail(
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001090 &stream->info.codec_param->enc_fmt,
1091 PJ_TRUE);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001092
1093 /* Init stream: */
1094 stream->endpt = endpt;
1095 stream->dir = info->dir;
1096 stream->user_data = user_data;
1097 stream->rtcp_interval = (PJMEDIA_RTCP_INTERVAL-500 + (pj_rand()%1000)) *
1098 info->codec_info.clock_rate / 1000;
1099
1100 stream->jb_last_frm = PJMEDIA_JB_NORMAL_FRAME;
1101
1102#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
1103 stream->use_ka = info->use_ka;
1104#endif
1105
1106 /* Build random RTCP CNAME. CNAME has user@host format */
1107 stream->cname.ptr = p = (char*) pj_pool_alloc(pool, 20);
1108 pj_create_random_string(p, 5);
1109 p += 5;
1110 *p++ = '@'; *p++ = 'p'; *p++ = 'j';
1111 pj_create_random_string(p, 6);
1112 p += 6;
1113 *p++ = '.'; *p++ = 'o'; *p++ = 'r'; *p++ = 'g';
1114 stream->cname.slen = p - stream->cname.ptr;
1115
1116
1117 /* Create mutex to protect jitter buffer: */
1118
1119 status = pj_mutex_create_simple(pool, NULL, &stream->jb_mutex);
1120 if (status != PJ_SUCCESS)
1121 return status;
1122
1123 /* Init codec param */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001124 stream->info.codec_param->dir = info->dir;
1125 stream->info.codec_param->enc_mtu = PJMEDIA_MAX_MTU -
1126 sizeof(pjmedia_rtp_hdr);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001127
1128 /* Init and open the codec. */
1129 status = stream->codec->op->init(stream->codec, pool);
1130 if (status != PJ_SUCCESS)
1131 return status;
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001132 status = stream->codec->op->open(stream->codec, stream->info.codec_param);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001133 if (status != PJ_SUCCESS)
1134 return status;
1135
1136 /* Get the frame size */
1137 stream->frame_size = vfd_enc->max_bps * vfd_enc->fps.denum /
1138 vfd_enc->fps.num;
1139
1140 /* As the maximum frame_size is not represented directly by maximum bps
1141 * (which includes intra and predicted frames), let's increase the
1142 * frame size value for safety.
1143 */
1144 stream->frame_size <<= 2;
1145
1146 /* Validate the frame size */
1147 if (stream->frame_size == 0 ||
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001148 stream->frame_size > PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE)
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001149 {
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001150 stream->frame_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE;
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001151 }
1152
1153 /* Get frame length in timestamp unit */
1154 stream->frame_ts_len = info->codec_info.clock_rate *
1155 vfd_enc->fps.denum / vfd_enc->fps.num;
1156
1157 /* Create decoder channel */
1158 status = create_channel( pool, stream, PJMEDIA_DIR_DECODING,
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001159 info->rx_pt, info, &stream->dec);
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001160 if (status != PJ_SUCCESS)
1161 return status;
1162
1163
1164 /* Create encoder channel */
1165 status = create_channel( pool, stream, PJMEDIA_DIR_ENCODING,
1166 info->tx_pt, info, &stream->enc);
1167 if (status != PJ_SUCCESS)
1168 return status;
1169
1170 /* Init jitter buffer parameters: */
1171 frm_ptime = 1000 * vfd_enc->fps.denum / vfd_enc->fps.num;
1172 chunks_per_frm = stream->frame_size / PJMEDIA_MAX_MTU;
1173
1174 /* JB max count, default 500ms */
1175 if (info->jb_max >= frm_ptime)
1176 jb_max = info->jb_max * chunks_per_frm / frm_ptime;
1177 else
1178 jb_max = 500 * chunks_per_frm / frm_ptime;
1179
1180 /* JB min prefetch, default 1 frame */
1181 if (info->jb_min_pre >= frm_ptime)
1182 jb_min_pre = info->jb_min_pre * chunks_per_frm / frm_ptime;
1183 else
1184 jb_min_pre = 1;
1185
1186 /* JB max prefetch, default 4/5 JB max count */
1187 if (info->jb_max_pre >= frm_ptime)
1188 jb_max_pre = info->jb_max_pre * chunks_per_frm / frm_ptime;
1189 else
1190 jb_max_pre = jb_max * 4 / 5;
1191
1192 /* JB init prefetch, default 0 */
1193 if (info->jb_init >= frm_ptime)
1194 jb_init = info->jb_init * chunks_per_frm / frm_ptime;
1195 else
1196 jb_init = 0;
1197
1198 /* Create jitter buffer */
1199 status = pjmedia_jbuf_create(pool, &stream->dec->port.info.name,
1200 PJMEDIA_MAX_MTU,
1201 1000 * vfd_enc->fps.denum / vfd_enc->fps.num,
1202 jb_max, &stream->jb);
1203 if (status != PJ_SUCCESS)
1204 return status;
1205
1206
1207 /* Set up jitter buffer */
1208 pjmedia_jbuf_set_adaptive( stream->jb, jb_init, jb_min_pre, jb_max_pre);
1209 //pjmedia_jbuf_enable_discard(stream->jb, PJ_FALSE);
1210
1211 /* Init RTCP session: */
1212 {
1213 pjmedia_rtcp_session_setting rtcp_setting;
1214
1215 pjmedia_rtcp_session_setting_default(&rtcp_setting);
1216 rtcp_setting.name = stream->name.ptr;
1217 rtcp_setting.ssrc = info->ssrc;
1218 rtcp_setting.rtp_ts_base = pj_ntohl(stream->enc->rtp.out_hdr.ts);
1219 rtcp_setting.clock_rate = info->codec_info.clock_rate;
1220 rtcp_setting.samples_per_frame = 1;
1221
1222 pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
1223 }
1224
1225 /* Only attach transport when stream is ready. */
1226 status = pjmedia_transport_attach(tp, stream, &info->rem_addr,
1227 &info->rem_rtcp,
1228 pj_sockaddr_get_len(&info->rem_addr),
1229 &on_rx_rtp, &on_rx_rtcp);
1230 if (status != PJ_SUCCESS)
1231 return status;
1232
1233 stream->transport = tp;
1234
1235 /* Send RTCP SDES */
1236 len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->buf,
1237 stream->enc->buf_size);
1238 if (len != 0) {
1239 pjmedia_transport_send_rtcp(stream->transport,
1240 stream->enc->buf, len);
1241 }
1242
1243#if defined(PJMEDIA_STREAM_ENABLE_KA) && PJMEDIA_STREAM_ENABLE_KA!=0
1244 /* NAT hole punching by sending KA packet via RTP transport. */
1245 if (stream->use_ka)
1246 send_keep_alive_packet(stream);
1247#endif
1248
1249#if TRACE_JB
1250 {
1251 char trace_name[PJ_MAXPATH];
1252 pj_ssize_t len;
1253
1254 pj_ansi_snprintf(trace_name, sizeof(trace_name),
1255 TRACE_JB_PATH_PREFIX "%s.csv",
1256 channel->port.info.name.ptr);
1257 status = pj_file_open(pool, trace_name, PJ_O_RDWR,
1258 &stream->trace_jb_fd);
1259 if (status != PJ_SUCCESS) {
1260 stream->trace_jb_fd = TRACE_JB_INVALID_FD;
1261 PJ_LOG(3,(THIS_FILE, "Failed creating RTP trace file '%s'",
1262 trace_name));
1263 } else {
1264 stream->trace_jb_buf = (char*)pj_pool_alloc(pool, PJ_LOG_MAX_SIZE);
1265
1266 /* Print column header */
1267 len = pj_ansi_snprintf(stream->trace_jb_buf, PJ_LOG_MAX_SIZE,
1268 "Time, Operation, Size, Frame Count, "
1269 "Frame type, RTP Seq, RTP TS, RTP M, "
1270 "JB size, JB burst level, JB prefetch\n");
1271 pj_file_write(stream->trace_jb_fd, stream->trace_jb_buf, &len);
1272 pj_file_flush(stream->trace_jb_fd);
1273 }
1274 }
1275#endif
1276
1277 /* Success! */
1278 *p_stream = stream;
1279
1280 PJ_LOG(5,(THIS_FILE, "Stream %s created", stream->name.ptr));
1281
1282 return PJ_SUCCESS;
1283}
1284
1285
1286/*
1287 * Destroy stream.
1288 */
1289PJ_DEF(pj_status_t) pjmedia_vid_stream_destroy( pjmedia_vid_stream *stream )
1290{
1291 unsigned len;
1292 PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
1293
1294#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1295 /* Send RTCP XR on stream destroy */
1296 if (stream->rtcp.xr_enabled) {
1297 int i;
1298 pjmedia_jb_state jb_state;
1299 void *rtcp_pkt;
1300 int len;
1301
1302 /* Update RTCP XR with current JB states */
1303 pjmedia_jbuf_get_state(stream->jb, &jb_state);
1304
1305 i = jb_state.avg_delay;
1306 pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
1307 PJMEDIA_RTCP_XR_INFO_JB_NOM,
1308 i);
1309
1310 i = jb_state.max_delay;
1311 pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
1312 PJMEDIA_RTCP_XR_INFO_JB_MAX,
1313 i);
1314
1315 /* Build RTCP XR packet */
1316 pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
1317 &rtcp_pkt, &len);
1318
1319 /* Send the RTCP XR to remote address */
1320 pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
1321
1322 /* Send the RTCP XR to third-party destination if specified */
1323 if (stream->rtcp_xr_dest_len) {
1324 pjmedia_transport_send_rtcp2(stream->transport,
1325 &stream->rtcp_xr_dest,
1326 stream->rtcp_xr_dest_len,
1327 rtcp_pkt, len);
1328 }
1329 }
1330#endif
1331
1332 /* Send RTCP BYE */
1333 if (stream->enc && stream->transport) {
1334 len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->buf,
1335 stream->enc->buf_size);
1336 if (len != 0) {
1337 pjmedia_transport_send_rtcp(stream->transport,
1338 stream->enc->buf, len);
1339 }
1340 }
1341
1342 /* Detach from transport
1343 * MUST NOT hold stream mutex while detaching from transport, as
1344 * it may cause deadlock. See ticket #460 for the details.
1345 */
1346 if (stream->transport) {
1347 pjmedia_transport_detach(stream->transport, stream);
1348 stream->transport = NULL;
1349 }
1350
1351 /* This function may be called when stream is partly initialized. */
1352 if (stream->jb_mutex)
1353 pj_mutex_lock(stream->jb_mutex);
1354
1355
1356 /* Free codec. */
1357 if (stream->codec) {
1358 stream->codec->op->close(stream->codec);
1359 pjmedia_vid_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec);
1360 stream->codec = NULL;
1361 }
1362
1363 /* Free mutex */
1364
1365 if (stream->jb_mutex) {
1366 pj_mutex_destroy(stream->jb_mutex);
1367 stream->jb_mutex = NULL;
1368 }
1369
1370 /* Destroy jitter buffer */
1371 if (stream->jb)
1372 pjmedia_jbuf_destroy(stream->jb);
1373
1374#if TRACE_JB
1375 if (TRACE_JB_OPENED(stream)) {
1376 pj_file_close(stream->trace_jb_fd);
1377 stream->trace_jb_fd = TRACE_JB_INVALID_FD;
1378 }
1379#endif
1380
1381 return PJ_SUCCESS;
1382}
1383
1384
1385
1386/*
1387 * Get the port interface.
1388 */
1389PJ_DEF(pj_status_t) pjmedia_vid_stream_get_port(pjmedia_vid_stream *stream,
1390 pjmedia_dir dir,
1391 pjmedia_port **p_port )
1392{
1393 PJ_ASSERT_RETURN(dir==PJMEDIA_DIR_ENCODING || dir==PJMEDIA_DIR_DECODING,
1394 PJ_EINVAL);
1395
1396 if (dir == PJMEDIA_DIR_ENCODING)
1397 *p_port = &stream->enc->port;
1398 else
1399 *p_port = &stream->dec->port;
1400
1401 return PJ_SUCCESS;
1402}
1403
1404
1405/*
1406 * Get the transport object
1407 */
1408PJ_DEF(pjmedia_transport*) pjmedia_vid_stream_get_transport(
1409 pjmedia_vid_stream *st)
1410{
1411 return st->transport;
1412}
1413
1414
1415/*
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001416 * Get stream statistics.
1417 */
1418PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat(
1419 const pjmedia_vid_stream *stream,
1420 pjmedia_rtcp_stat *stat)
1421{
1422 PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
1423
1424 pj_memcpy(stat, &stream->rtcp.stat, sizeof(pjmedia_rtcp_stat));
1425 return PJ_SUCCESS;
1426}
1427
1428
1429/*
1430 * Reset the stream statistics in the middle of a stream session.
1431 */
1432PJ_DEF(pj_status_t) pjmedia_vid_stream_reset_stat(pjmedia_vid_stream *stream)
1433{
1434 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1435
1436 pjmedia_rtcp_init_stat(&stream->rtcp.stat);
1437
1438 return PJ_SUCCESS;
1439}
1440
1441
1442#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1443/*
1444 * Get stream extended statistics.
1445 */
1446PJ_DEF(pj_status_t) pjmedia_stream_get_stat_xr(
1447 const pjmedia_vid_stream *stream,
1448 pjmedia_rtcp_xr_stat *stat)
1449{
1450 PJ_ASSERT_RETURN(stream && stat, PJ_EINVAL);
1451
1452 if (stream->rtcp.xr_enabled) {
1453 pj_memcpy(stat, &stream->rtcp.xr_session.stat,
1454 sizeof(pjmedia_rtcp_xr_stat));
1455 return PJ_SUCCESS;
1456 }
1457 return PJ_ENOTFOUND;
1458}
1459#endif
1460
1461/*
1462 * Get jitter buffer state.
1463 */
1464PJ_DEF(pj_status_t) pjmedia_vid_stream_get_stat_jbuf(
1465 const pjmedia_vid_stream *stream,
1466 pjmedia_jb_state *state)
1467{
1468 PJ_ASSERT_RETURN(stream && state, PJ_EINVAL);
1469 return pjmedia_jbuf_get_state(stream->jb, state);
1470}
1471
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001472
1473/*
1474 * Get the stream info.
1475 */
1476PJ_DEF(pj_status_t) pjmedia_vid_stream_get_info(
1477 const pjmedia_vid_stream *stream,
1478 pjmedia_vid_stream_info *info)
1479{
1480 PJ_ASSERT_RETURN(stream && info, PJ_EINVAL);
1481 pj_memcpy(info, &stream->info, sizeof(*info));
1482 return PJ_SUCCESS;
1483}
1484
1485
1486/*
1487 * Start stream.
1488 */
1489PJ_DEF(pj_status_t) pjmedia_vid_stream_start(pjmedia_vid_stream *stream)
1490{
1491
1492 PJ_ASSERT_RETURN(stream && stream->enc && stream->dec, PJ_EINVALIDOP);
1493
1494 if (stream->enc && (stream->dir & PJMEDIA_DIR_ENCODING)) {
1495 stream->enc->paused = 0;
1496 //pjmedia_snd_stream_start(stream->enc->snd_stream);
1497 PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream started"));
1498 } else {
1499 PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
1500 }
1501
1502 if (stream->dec && (stream->dir & PJMEDIA_DIR_DECODING)) {
1503 stream->dec->paused = 0;
1504 //pjmedia_snd_stream_start(stream->dec->snd_stream);
1505 PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream started"));
1506 } else {
1507 PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
1508 }
1509
1510 return PJ_SUCCESS;
1511}
1512
1513
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001514/*
1515 * Pause stream.
1516 */
1517PJ_DEF(pj_status_t) pjmedia_vid_stream_pause(pjmedia_vid_stream *stream,
1518 pjmedia_dir dir)
1519{
1520 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1521
1522 if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
1523 stream->enc->paused = 1;
1524 PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream paused"));
1525 }
1526
1527 if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
1528 stream->dec->paused = 1;
1529
1530 /* Also reset jitter buffer */
1531 pj_mutex_lock( stream->jb_mutex );
1532 pjmedia_jbuf_reset(stream->jb);
1533 pj_mutex_unlock( stream->jb_mutex );
1534
1535 PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream paused"));
1536 }
1537
1538 return PJ_SUCCESS;
1539}
1540
1541
1542/*
1543 * Resume stream
1544 */
1545PJ_DEF(pj_status_t) pjmedia_vid_stream_resume(pjmedia_vid_stream *stream,
1546 pjmedia_dir dir)
1547{
1548 PJ_ASSERT_RETURN(stream, PJ_EINVAL);
1549
1550 if ((dir & PJMEDIA_DIR_ENCODING) && stream->enc) {
1551 stream->enc->paused = 0;
1552 PJ_LOG(4,(stream->enc->port.info.name.ptr, "Encoder stream resumed"));
1553 }
1554
1555 if ((dir & PJMEDIA_DIR_DECODING) && stream->dec) {
1556 stream->dec->paused = 0;
1557 PJ_LOG(4,(stream->dec->port.info.name.ptr, "Decoder stream resumed"));
1558 }
1559
1560 return PJ_SUCCESS;
1561}
1562
1563
1564static const pj_str_t ID_AUDIO = { "audio", 5};
1565static const pj_str_t ID_VIDEO = { "video", 5};
1566static const pj_str_t ID_APPLICATION = { "application", 11};
1567static const pj_str_t ID_IN = { "IN", 2 };
1568static const pj_str_t ID_IP4 = { "IP4", 3};
1569static const pj_str_t ID_IP6 = { "IP6", 3};
1570static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
1571static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
1572//static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
1573static const pj_str_t ID_RTPMAP = { "rtpmap", 6 };
1574static const pj_str_t ID_TELEPHONE_EVENT = { "telephone-event", 15 };
1575
1576static const pj_str_t STR_INACTIVE = { "inactive", 8 };
1577static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
1578static const pj_str_t STR_SENDONLY = { "sendonly", 8 };
1579static const pj_str_t STR_RECVONLY = { "recvonly", 8 };
1580
1581
1582/*
1583 * Internal function for collecting codec info and param from the SDP media.
1584 */
1585static pj_status_t get_video_codec_info_param(pjmedia_vid_stream_info *si,
1586 pj_pool_t *pool,
1587 pjmedia_vid_codec_mgr *mgr,
1588 const pjmedia_sdp_media *local_m,
1589 const pjmedia_sdp_media *rem_m)
1590{
1591 const pjmedia_sdp_attr *attr;
1592 pjmedia_sdp_rtpmap *rtpmap;
1593 unsigned i, pt = 0;
1594 pj_status_t status;
1595
1596 /* Get codec info.
1597 * For static payload types, get the info from codec manager.
1598 * For dynamic payload types, MUST get the rtpmap.
1599 */
1600 pt = pj_strtoul(&local_m->desc.fmt[0]);
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001601 si->rx_pt = pt;
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001602 if (pt < 96) {
1603 const pjmedia_vid_codec_info *p_info;
1604
1605 status = pjmedia_vid_codec_mgr_get_codec_info(mgr, pt, &p_info);
1606 if (status != PJ_SUCCESS)
1607 return status;
1608
1609 si->codec_info = *p_info;
1610
1611 /* For static payload type, pt's are symetric */
1612 si->tx_pt = pt;
1613
1614 } else {
1615 unsigned info_cnt = 1;
1616 const pjmedia_vid_codec_info *p_info;
1617
1618 attr = pjmedia_sdp_media_find_attr(local_m, &ID_RTPMAP,
1619 &local_m->desc.fmt[0]);
1620 if (attr == NULL)
1621 return PJMEDIA_EMISSINGRTPMAP;
1622
1623 status = pjmedia_sdp_attr_to_rtpmap(pool, attr, &rtpmap);
1624 if (status != PJ_SUCCESS)
1625 return status;
1626
1627 /* Get codec info from codec id */
1628 status = pjmedia_vid_codec_mgr_find_codecs_by_id(
1629 mgr, &rtpmap->enc_name,
1630 &info_cnt, &p_info, NULL);
1631 if (status != PJ_SUCCESS)
1632 return status;
1633
1634 si->codec_info = *p_info;
1635
1636 /* Determine payload type for outgoing channel, by finding
1637 * dynamic payload type in remote SDP that matches the answer.
1638 */
1639 si->tx_pt = 0xFFFF;
1640 for (i=0; i<rem_m->desc.fmt_count; ++i) {
1641 unsigned rpt;
1642 pjmedia_sdp_attr *r_attr;
1643 pjmedia_sdp_rtpmap r_rtpmap;
1644
1645 rpt = pj_strtoul(&rem_m->desc.fmt[i]);
1646 if (rpt < 96)
1647 continue;
1648
1649 r_attr = pjmedia_sdp_media_find_attr(rem_m, &ID_RTPMAP,
1650 &rem_m->desc.fmt[i]);
1651 if (!r_attr)
1652 continue;
1653
1654 if (pjmedia_sdp_attr_get_rtpmap(r_attr, &r_rtpmap) != PJ_SUCCESS)
1655 continue;
1656
1657 if (!pj_stricmp(&rtpmap->enc_name, &r_rtpmap.enc_name) &&
1658 rtpmap->clock_rate == r_rtpmap.clock_rate)
1659 {
1660 /* Found matched codec. */
1661 si->tx_pt = rpt;
1662
1663 break;
1664 }
1665 }
1666
1667 if (si->tx_pt == 0xFFFF)
1668 return PJMEDIA_EMISSINGRTPMAP;
1669 }
1670
1671
1672 /* Now that we have codec info, get the codec param. */
1673 si->codec_param = PJ_POOL_ALLOC_T(pool, pjmedia_vid_codec_param);
1674 status = pjmedia_vid_codec_mgr_get_default_param(mgr,
1675 &si->codec_info,
1676 si->codec_param);
1677
1678 /* Get remote fmtp for our encoder. */
1679 pjmedia_stream_info_parse_fmtp(pool, rem_m, si->tx_pt,
1680 &si->codec_param->enc_fmtp);
1681
1682 /* Get local fmtp for our decoder. */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001683 pjmedia_stream_info_parse_fmtp(pool, local_m, si->rx_pt,
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001684 &si->codec_param->dec_fmtp);
1685
1686 /* When direction is NONE (it means SDP negotiation has failed) we don't
1687 * need to return a failure here, as returning failure will cause
1688 * the whole SDP to be rejected. See ticket #:
1689 * http://
1690 *
1691 * Thanks Alain Totouom
1692 */
1693 if (status != PJ_SUCCESS && si->dir != PJMEDIA_DIR_NONE)
1694 return status;
1695
1696 return PJ_SUCCESS;
1697}
1698
1699
1700
1701/*
1702 * Create stream info from SDP media line.
1703 */
1704PJ_DEF(pj_status_t) pjmedia_vid_stream_info_from_sdp(
1705 pjmedia_vid_stream_info *si,
1706 pj_pool_t *pool,
1707 pjmedia_endpt *endpt,
1708 const pjmedia_sdp_session *local,
1709 const pjmedia_sdp_session *remote,
1710 unsigned stream_idx)
1711{
1712 const pjmedia_sdp_attr *attr;
1713 const pjmedia_sdp_media *local_m;
1714 const pjmedia_sdp_media *rem_m;
1715 const pjmedia_sdp_conn *local_conn;
1716 const pjmedia_sdp_conn *rem_conn;
1717 int rem_af, local_af;
1718 pj_sockaddr local_addr;
1719 pj_status_t status;
1720
1721 PJ_UNUSED_ARG(endpt);
1722
1723 /* Validate arguments: */
1724 PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
1725 PJ_ASSERT_RETURN(stream_idx < local->media_count, PJ_EINVAL);
1726 PJ_ASSERT_RETURN(stream_idx < remote->media_count, PJ_EINVAL);
1727
1728 /* Keep SDP shortcuts */
1729 local_m = local->media[stream_idx];
1730 rem_m = remote->media[stream_idx];
1731
1732 local_conn = local_m->conn ? local_m->conn : local->conn;
1733 if (local_conn == NULL)
1734 return PJMEDIA_SDP_EMISSINGCONN;
1735
1736 rem_conn = rem_m->conn ? rem_m->conn : remote->conn;
1737 if (rem_conn == NULL)
1738 return PJMEDIA_SDP_EMISSINGCONN;
1739
1740 /* Media type must be audio */
Nanang Izzuddin235e1b42011-02-28 18:59:47 +00001741 if (pj_stricmp(&local_m->desc.media, &ID_VIDEO) != 0)
Nanang Izzuddina9c1cf42011-02-24 07:47:55 +00001742 return PJMEDIA_EINVALIMEDIATYPE;
1743
1744
1745 /* Reset: */
1746
1747 pj_bzero(si, sizeof(*si));
1748
1749#if PJMEDIA_HAS_RTCP_XR && PJMEDIA_STREAM_ENABLE_XR
1750 /* Set default RTCP XR enabled/disabled */
1751 si->rtcp_xr_enabled = PJ_TRUE;
1752#endif
1753
1754 /* Media type: */
1755 si->type = PJMEDIA_TYPE_VIDEO;
1756
1757 /* Transport protocol */
1758
1759 /* At this point, transport type must be compatible,
1760 * the transport instance will do more validation later.
1761 */
1762 status = pjmedia_sdp_transport_cmp(&rem_m->desc.transport,
1763 &local_m->desc.transport);
1764 if (status != PJ_SUCCESS)
1765 return PJMEDIA_SDPNEG_EINVANSTP;
1766
1767 if (pj_stricmp(&local_m->desc.transport, &ID_RTP_AVP) == 0) {
1768
1769 si->proto = PJMEDIA_TP_PROTO_RTP_AVP;
1770
1771 } else if (pj_stricmp(&local_m->desc.transport, &ID_RTP_SAVP) == 0) {
1772
1773 si->proto = PJMEDIA_TP_PROTO_RTP_SAVP;
1774
1775 } else {
1776
1777 si->proto = PJMEDIA_TP_PROTO_UNKNOWN;
1778 return PJ_SUCCESS;
1779 }
1780
1781
1782 /* Check address family in remote SDP */
1783 rem_af = pj_AF_UNSPEC();
1784 if (pj_stricmp(&rem_conn->net_type, &ID_IN)==0) {
1785 if (pj_stricmp(&rem_conn->addr_type, &ID_IP4)==0) {
1786 rem_af = pj_AF_INET();
1787 } else if (pj_stricmp(&rem_conn->addr_type, &ID_IP6)==0) {
1788 rem_af = pj_AF_INET6();
1789 }
1790 }
1791
1792 if (rem_af==pj_AF_UNSPEC()) {
1793 /* Unsupported address family */
1794 return PJ_EAFNOTSUP;
1795 }
1796
1797 /* Set remote address: */
1798 status = pj_sockaddr_init(rem_af, &si->rem_addr, &rem_conn->addr,
1799 rem_m->desc.port);
1800 if (status != PJ_SUCCESS) {
1801 /* Invalid IP address. */
1802 return PJMEDIA_EINVALIDIP;
1803 }
1804
1805 /* Check address family of local info */
1806 local_af = pj_AF_UNSPEC();
1807 if (pj_stricmp(&local_conn->net_type, &ID_IN)==0) {
1808 if (pj_stricmp(&local_conn->addr_type, &ID_IP4)==0) {
1809 local_af = pj_AF_INET();
1810 } else if (pj_stricmp(&local_conn->addr_type, &ID_IP6)==0) {
1811 local_af = pj_AF_INET6();
1812 }
1813 }
1814
1815 if (local_af==pj_AF_UNSPEC()) {
1816 /* Unsupported address family */
1817 return PJ_SUCCESS;
1818 }
1819
1820 /* Set remote address: */
1821 status = pj_sockaddr_init(local_af, &local_addr, &local_conn->addr,
1822 local_m->desc.port);
1823 if (status != PJ_SUCCESS) {
1824 /* Invalid IP address. */
1825 return PJMEDIA_EINVALIDIP;
1826 }
1827
1828 /* Local and remote address family must match */
1829 if (local_af != rem_af)
1830 return PJ_EAFNOTSUP;
1831
1832 /* Media direction: */
1833
1834 if (local_m->desc.port == 0 ||
1835 pj_sockaddr_has_addr(&local_addr)==PJ_FALSE ||
1836 pj_sockaddr_has_addr(&si->rem_addr)==PJ_FALSE ||
1837 pjmedia_sdp_media_find_attr(local_m, &STR_INACTIVE, NULL)!=NULL)
1838 {
1839 /* Inactive stream. */
1840
1841 si->dir = PJMEDIA_DIR_NONE;
1842
1843 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_SENDONLY, NULL)!=NULL) {
1844
1845 /* Send only stream. */
1846
1847 si->dir = PJMEDIA_DIR_ENCODING;
1848
1849 } else if (pjmedia_sdp_media_find_attr(local_m, &STR_RECVONLY, NULL)!=NULL) {
1850
1851 /* Recv only stream. */
1852
1853 si->dir = PJMEDIA_DIR_DECODING;
1854
1855 } else {
1856
1857 /* Send and receive stream. */
1858
1859 si->dir = PJMEDIA_DIR_ENCODING_DECODING;
1860
1861 }
1862
1863 /* No need to do anything else if stream is rejected */
1864 if (local_m->desc.port == 0) {
1865 return PJ_SUCCESS;
1866 }
1867
1868 /* If "rtcp" attribute is present in the SDP, set the RTCP address
1869 * from that attribute. Otherwise, calculate from RTP address.
1870 */
1871 attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
1872 "rtcp", NULL);
1873 if (attr) {
1874 pjmedia_sdp_rtcp_attr rtcp;
1875 status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
1876 if (status == PJ_SUCCESS) {
1877 if (rtcp.addr.slen) {
1878 status = pj_sockaddr_init(rem_af, &si->rem_rtcp, &rtcp.addr,
1879 (pj_uint16_t)rtcp.port);
1880 } else {
1881 pj_sockaddr_init(rem_af, &si->rem_rtcp, NULL,
1882 (pj_uint16_t)rtcp.port);
1883 pj_memcpy(pj_sockaddr_get_addr(&si->rem_rtcp),
1884 pj_sockaddr_get_addr(&si->rem_addr),
1885 pj_sockaddr_get_addr_len(&si->rem_addr));
1886 }
1887 }
1888 }
1889
1890 if (!pj_sockaddr_has_addr(&si->rem_rtcp)) {
1891 int rtcp_port;
1892
1893 pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr));
1894 rtcp_port = pj_sockaddr_get_port(&si->rem_addr) + 1;
1895 pj_sockaddr_set_port(&si->rem_rtcp, (pj_uint16_t)rtcp_port);
1896 }
1897
1898 /* Get codec info and param */
1899 status = get_video_codec_info_param(si, pool, NULL, local_m, rem_m);
1900
1901 /* Leave SSRC to random. */
1902 si->ssrc = pj_rand();
1903
1904 /* Set default jitter buffer parameter. */
1905 si->jb_init = si->jb_max = si->jb_min_pre = si->jb_max_pre = -1;
1906
1907 return status;
1908}
1909