Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip-apps / src / samples / vid_streamutil.c
1 /* $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
20
21 /**
22  * \page page_pjmedia_samples_vid_streamutil_c Samples: Video Streaming
23  *
24  * This example mainly demonstrates how to stream video to remote
25  * peer using RTP.
26  *
27  * This file is pjsip-apps/src/samples/vid_streamutil.c
28  *
29  * \includelineno vid_streamutil.c
30  */
31
32 #include <pjlib.h>
33 #include <pjlib-util.h>
34 #include <pjmedia.h>
35 #include <pjmedia-codec.h>
36 #include <pjmedia/transport_srtp.h>
37
38
39 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
40
41
42 #include <stdlib.h>     /* atoi() */
43 #include <stdio.h>
44
45 #include "util.h"
46
47
48 static const char *desc = 
49  " vid_streamutil                                                       \n"
50  "\n"
51  " PURPOSE:                                                             \n"
52  "  Demonstrate how to use pjmedia video stream component to            \n"
53  "  transmit/receive RTP packets to/from video device/file.             \n"
54  "\n"
55  "\n"
56  " USAGE:                                                               \n"
57  "  vid_streamutil [options]                                            \n"
58  "\n"
59  "\n"
60  " Options:                                                             \n"
61  "  --codec=CODEC         Set the codec name.                           \n"
62  "  --local-port=PORT     Set local RTP port (default=4000)             \n"
63  "  --remote=IP:PORT      Set the remote peer. If this option is set,   \n"
64  "                        the program will transmit RTP audio to the    \n"
65  "                        specified address. (default: recv only)       \n"
66  "  --play-file=AVI       Send video from the AVI file instead of from  \n"
67  "                        the video device.                             \n"
68  "  --send-recv           Set stream direction to bidirectional.        \n"
69  "  --send-only           Set stream direction to send only             \n"
70  "  --recv-only           Set stream direction to recv only (default)   \n"
71  
72  "  --send-width          Video width to be sent                        \n"
73  "  --send-height         Video height to be sent                       \n"
74  "                        --send-width and --send-height not applicable \n"
75  "                        for file streaming (see --play-file)          \n"
76
77  "  --send-pt             Payload type for sending                      \n"
78  "  --recv-pt             Payload type for receiving                    \n"
79
80 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
81  "  --use-srtp[=NAME]     Enable SRTP with crypto suite NAME            \n"
82  "                        e.g: AES_CM_128_HMAC_SHA1_80 (default),       \n"
83  "                             AES_CM_128_HMAC_SHA1_32                  \n"
84  "                        Use this option along with the TX & RX keys,  \n"
85  "                        formated of 60 hex digits (e.g: E148DA..)     \n"
86  "  --srtp-tx-key         SRTP key for transmiting                      \n"
87  "  --srtp-rx-key         SRTP key for receiving                        \n"
88 #endif
89
90  "\n"
91 ;
92
93 #define THIS_FILE       "vid_streamutil.c"
94
95
96 /* If set, local renderer will be created to play original file */
97 #define HAS_LOCAL_RENDERER_FOR_PLAY_FILE    1
98
99
100 /* Default width and height for the renderer, better be set to maximum
101  * acceptable size.
102  */
103 #define DEF_RENDERER_WIDTH                  640
104 #define DEF_RENDERER_HEIGHT                 480
105
106
107 /* Prototype */
108 static void print_stream_stat(pjmedia_vid_stream *stream,
109                               const pjmedia_vid_codec_param *codec_param);
110
111 /* Prototype for LIBSRTP utility in file datatypes.c */
112 int hex_string_to_octet_string(char *raw, char *hex, int len);
113
114 /* 
115  * Register all codecs. 
116  */
117 static pj_status_t init_codecs(pj_pool_factory *pf)
118 {
119     pj_status_t status;
120
121     /* To suppress warning about unused var when all codecs are disabled */
122     PJ_UNUSED_ARG(status);
123
124 #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
125     status = pjmedia_codec_ffmpeg_vid_init(NULL, pf);
126     PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
127 #endif
128
129     return PJ_SUCCESS;
130 }
131
132 /* 
133  * Register all codecs. 
134  */
135 static void deinit_codecs()
136 {
137 #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0
138     pjmedia_codec_ffmpeg_vid_deinit();
139 #endif
140 }
141
142 static pj_status_t create_file_player( pj_pool_t *pool,
143                                        const char *file_name,
144                                        pjmedia_port **p_play_port)
145 {
146     pjmedia_avi_streams *avi_streams;
147     pjmedia_avi_stream *vid_stream;
148     pjmedia_port *play_port;
149     pj_status_t status;
150
151     status = pjmedia_avi_player_create_streams(pool, file_name, 0, &avi_streams);
152     if (status != PJ_SUCCESS)
153         return status;
154
155     vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
156                                                          0,
157                                                          PJMEDIA_TYPE_VIDEO);
158     if (!vid_stream)
159         return PJ_ENOTFOUND;
160
161     play_port = pjmedia_avi_stream_get_port(vid_stream);
162     pj_assert(play_port);
163
164     *p_play_port = play_port;
165
166     return PJ_SUCCESS;
167 }
168
169 /* 
170  * Create stream based on the codec, dir, remote address, etc. 
171  */
172 static pj_status_t create_stream( pj_pool_t *pool,
173                                   pjmedia_endpt *med_endpt,
174                                   const pjmedia_vid_codec_info *codec_info,
175                                   pjmedia_vid_codec_param *codec_param,
176                                   pjmedia_dir dir,
177                                   pj_int8_t rx_pt,
178                                   pj_int8_t tx_pt,
179                                   pj_uint16_t local_port,
180                                   const pj_sockaddr_in *rem_addr,
181 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
182                                   pj_bool_t use_srtp,
183                                   const pj_str_t *crypto_suite,
184                                   const pj_str_t *srtp_tx_key,
185                                   const pj_str_t *srtp_rx_key,
186 #endif
187                                   pjmedia_vid_stream **p_stream )
188 {
189     pjmedia_vid_stream_info info;
190     pjmedia_transport *transport = NULL;
191     pj_status_t status;
192 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
193     pjmedia_transport *srtp_tp = NULL;
194 #endif
195
196     /* Reset stream info. */
197     pj_bzero(&info, sizeof(info));
198
199     /* Initialize stream info formats */
200     info.type = PJMEDIA_TYPE_VIDEO;
201     info.dir = dir;
202     info.codec_info = *codec_info;
203     info.tx_pt = (tx_pt == -1)? codec_info->pt : tx_pt;
204     info.rx_pt = (rx_pt == -1)? codec_info->pt : rx_pt;
205     info.ssrc = pj_rand();
206     if (codec_param)
207         info.codec_param = codec_param;
208     
209     /* Copy remote address */
210     pj_memcpy(&info.rem_addr, rem_addr, sizeof(pj_sockaddr_in));
211
212     /* If remote address is not set, set to an arbitrary address
213      * (otherwise stream will assert).
214      */
215     if (info.rem_addr.addr.sa_family == 0) {
216         const pj_str_t addr = pj_str("127.0.0.1");
217         pj_sockaddr_in_init(&info.rem_addr.ipv4, &addr, 0);
218     }
219
220     /* Create media transport */
221     status = pjmedia_transport_udp_create(med_endpt, NULL, local_port,
222                                           0, &transport);
223     if (status != PJ_SUCCESS)
224         return status;
225
226 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
227     /* Check if SRTP enabled */
228     if (use_srtp) {
229         pjmedia_srtp_crypto tx_plc, rx_plc;
230
231         status = pjmedia_transport_srtp_create(med_endpt, transport, 
232                                                NULL, &srtp_tp);
233         if (status != PJ_SUCCESS)
234             return status;
235
236         pj_bzero(&tx_plc, sizeof(pjmedia_srtp_crypto));
237         pj_bzero(&rx_plc, sizeof(pjmedia_srtp_crypto));
238
239         tx_plc.key = *srtp_tx_key;
240         tx_plc.name = *crypto_suite;
241         rx_plc.key = *srtp_rx_key;
242         rx_plc.name = *crypto_suite;
243         
244         status = pjmedia_transport_srtp_start(srtp_tp, &tx_plc, &rx_plc);
245         if (status != PJ_SUCCESS)
246             return status;
247
248         transport = srtp_tp;
249     }
250 #endif
251
252     /* Now that the stream info is initialized, we can create the 
253      * stream.
254      */
255
256     status = pjmedia_vid_stream_create( med_endpt, pool, &info, 
257                                         transport, 
258                                         NULL, p_stream);
259
260     if (status != PJ_SUCCESS) {
261         app_perror(THIS_FILE, "Error creating stream", status);
262         pjmedia_transport_close(transport);
263         return status;
264     }
265
266
267     return PJ_SUCCESS;
268 }
269
270
271 typedef struct play_file_data
272 {
273     const char *file_name;
274     pjmedia_port *play_port;
275     pjmedia_port *stream_port;
276     pjmedia_vid_codec *decoder;
277     pjmedia_port *renderer;
278     void *read_buf;
279     pj_size_t read_buf_size;
280     void *dec_buf;
281     pj_size_t dec_buf_size;
282 } play_file_data;
283
284
285 static void clock_cb(const pj_timestamp *ts, void *user_data)
286 {
287     play_file_data *play_file = (play_file_data*)user_data;
288     pjmedia_frame read_frame, write_frame;
289     pj_status_t status;
290
291     PJ_UNUSED_ARG(ts);
292
293     /* Read frame from file */
294     read_frame.buf = play_file->read_buf;
295     read_frame.size = play_file->read_buf_size;
296     pjmedia_port_get_frame(play_file->play_port, &read_frame);
297
298     /* Decode frame, if needed */
299     if (play_file->decoder) {
300         pjmedia_vid_codec *decoder = play_file->decoder;
301
302         write_frame.buf = play_file->dec_buf;
303         write_frame.size = play_file->dec_buf_size;
304         status = pjmedia_vid_codec_decode(decoder, 1, &read_frame,
305                                           write_frame.size, &write_frame);
306         if (status != PJ_SUCCESS)
307             return;
308     } else {
309         write_frame = read_frame;
310     }
311
312     /* Display frame locally */
313     if (play_file->renderer)
314         pjmedia_port_put_frame(play_file->renderer, &write_frame);
315
316     /* Send frame */
317     pjmedia_port_put_frame(play_file->stream_port, &write_frame);
318 }
319
320
321 /*
322  * usage()
323  */
324 static void usage()
325 {
326     puts(desc);
327 }
328
329 /*
330  * main()
331  */
332 int main(int argc, char *argv[])
333 {
334     pj_caching_pool cp;
335     pjmedia_endpt *med_endpt;
336     pj_pool_t *pool;
337     pjmedia_vid_stream *stream = NULL;
338     pjmedia_port *enc_port, *dec_port;
339     pj_status_t status; 
340
341     pjmedia_vid_port *capture=NULL, *renderer=NULL;
342     pjmedia_vid_port_param vpp;
343
344 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
345     /* SRTP variables */
346     pj_bool_t use_srtp = PJ_FALSE;
347     char tmp_tx_key[64];
348     char tmp_rx_key[64];
349     pj_str_t  srtp_tx_key = {NULL, 0};
350     pj_str_t  srtp_rx_key = {NULL, 0};
351     pj_str_t  srtp_crypto_suite = {NULL, 0};
352     int tmp_key_len;
353 #endif
354
355     /* Default values */
356     const pjmedia_vid_codec_info *codec_info;
357     pjmedia_vid_codec_param codec_param;
358     pjmedia_dir dir = PJMEDIA_DIR_DECODING;
359     pj_sockaddr_in remote_addr;
360     pj_uint16_t local_port = 4000;
361     char *codec_id = NULL;
362     pjmedia_rect_size tx_size = {0};
363     pj_int8_t rx_pt = -1, tx_pt = -1;
364
365     play_file_data play_file = { NULL };
366     pjmedia_port *play_port = NULL;
367     pjmedia_vid_codec *play_decoder = NULL;
368     pjmedia_clock *play_clock = NULL;
369
370     enum {
371         OPT_CODEC       = 'c',
372         OPT_LOCAL_PORT  = 'p',
373         OPT_REMOTE      = 'r',
374         OPT_PLAY_FILE   = 'f',
375         OPT_SEND_RECV   = 'b',
376         OPT_SEND_ONLY   = 's',
377         OPT_RECV_ONLY   = 'i',
378         OPT_SEND_WIDTH  = 'W',
379         OPT_SEND_HEIGHT = 'H',
380         OPT_RECV_PT     = 't',
381         OPT_SEND_PT     = 'T',
382 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
383         OPT_USE_SRTP    = 'S',
384 #endif
385         OPT_SRTP_TX_KEY = 'x',
386         OPT_SRTP_RX_KEY = 'y',
387         OPT_HELP        = 'h',
388     };
389
390     struct pj_getopt_option long_options[] = {
391         { "codec",          1, 0, OPT_CODEC },
392         { "local-port",     1, 0, OPT_LOCAL_PORT },
393         { "remote",         1, 0, OPT_REMOTE },
394         { "play-file",      1, 0, OPT_PLAY_FILE },
395         { "send-recv",      0, 0, OPT_SEND_RECV },
396         { "send-only",      0, 0, OPT_SEND_ONLY },
397         { "recv-only",      0, 0, OPT_RECV_ONLY },
398         { "send-width",     1, 0, OPT_SEND_WIDTH },
399         { "send-height",    1, 0, OPT_SEND_HEIGHT },
400         { "recv-pt",        1, 0, OPT_RECV_PT },
401         { "send-pt",        1, 0, OPT_SEND_PT },
402 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
403         { "use-srtp",       2, 0, OPT_USE_SRTP },
404         { "srtp-tx-key",    1, 0, OPT_SRTP_TX_KEY },
405         { "srtp-rx-key",    1, 0, OPT_SRTP_RX_KEY },
406 #endif
407         { "help",           0, 0, OPT_HELP },
408         { NULL, 0, 0, 0 },
409     };
410
411     int c;
412     int option_index;
413
414
415     pj_bzero(&remote_addr, sizeof(remote_addr));
416
417
418     /* init PJLIB : */
419     status = pj_init();
420     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
421
422
423     /* Parse arguments */
424     pj_optind = 0;
425     while((c=pj_getopt_long(argc,argv, "h", long_options, &option_index))!=-1)
426     {
427         switch (c) {
428         case OPT_CODEC:
429             codec_id = pj_optarg;
430             break;
431
432         case OPT_LOCAL_PORT:
433             local_port = (pj_uint16_t) atoi(pj_optarg);
434             if (local_port < 1) {
435                 printf("Error: invalid local port %s\n", pj_optarg);
436                 return 1;
437             }
438             break;
439
440         case OPT_REMOTE:
441             {
442                 pj_str_t ip = pj_str(strtok(pj_optarg, ":"));
443                 pj_uint16_t port = (pj_uint16_t) atoi(strtok(NULL, ":"));
444
445                 status = pj_sockaddr_in_init(&remote_addr, &ip, port);
446                 if (status != PJ_SUCCESS) {
447                     app_perror(THIS_FILE, "Invalid remote address", status);
448                     return 1;
449                 }
450             }
451             break;
452
453         case OPT_PLAY_FILE:
454             play_file.file_name = pj_optarg;
455             break;
456
457         case OPT_SEND_RECV:
458             dir = PJMEDIA_DIR_ENCODING_DECODING;
459             break;
460
461         case OPT_SEND_ONLY:
462             dir = PJMEDIA_DIR_ENCODING;
463             break;
464
465         case OPT_RECV_ONLY:
466             dir = PJMEDIA_DIR_DECODING;
467             break;
468
469         case OPT_SEND_WIDTH:
470             tx_size.w = (unsigned)atoi(pj_optarg);
471             break;
472
473         case OPT_SEND_HEIGHT:
474             tx_size.h = (unsigned)atoi(pj_optarg);
475             break;
476
477         case OPT_RECV_PT:
478             rx_pt = (pj_int8_t)atoi(pj_optarg);
479             break;
480
481         case OPT_SEND_PT:
482             tx_pt = (pj_int8_t)atoi(pj_optarg);
483             break;
484
485 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
486         case OPT_USE_SRTP:
487             use_srtp = PJ_TRUE;
488             if (pj_optarg) {
489                 pj_strset(&srtp_crypto_suite, pj_optarg, strlen(pj_optarg));
490             } else {
491                 srtp_crypto_suite = pj_str("AES_CM_128_HMAC_SHA1_80");
492             }
493             break;
494
495         case OPT_SRTP_TX_KEY:
496             tmp_key_len = hex_string_to_octet_string(tmp_tx_key, pj_optarg, 
497                                                      strlen(pj_optarg));
498             pj_strset(&srtp_tx_key, tmp_tx_key, tmp_key_len/2);
499             break;
500
501         case OPT_SRTP_RX_KEY:
502             tmp_key_len = hex_string_to_octet_string(tmp_rx_key, pj_optarg,
503                                                      strlen(pj_optarg));
504             pj_strset(&srtp_rx_key, tmp_rx_key, tmp_key_len/2);
505             break;
506 #endif
507
508         case OPT_HELP:
509             usage();
510             return 1;
511
512         default:
513             printf("Invalid options %s\n", argv[pj_optind]);
514             return 1;
515         }
516
517     }
518
519
520     /* Verify arguments. */
521     if (dir & PJMEDIA_DIR_ENCODING) {
522         if (remote_addr.sin_addr.s_addr == 0) {
523             printf("Error: remote address must be set\n");
524             return 1;
525         }
526     }
527
528     if (play_file.file_name != NULL && dir != PJMEDIA_DIR_ENCODING) {
529         printf("Direction is set to --send-only because of --play-file\n");
530         dir = PJMEDIA_DIR_ENCODING;
531     }
532
533 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
534     /* SRTP validation */
535     if (use_srtp) {
536         if (!srtp_tx_key.slen || !srtp_rx_key.slen)
537         {
538             printf("Error: Key for each SRTP stream direction must be set\n");
539             return 1;
540         }
541     }
542 #endif
543
544     /* Must create a pool factory before we can allocate any memory. */
545     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
546
547     /* 
548      * Initialize media endpoint.
549      * This will implicitly initialize PJMEDIA too.
550      */
551     status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt);
552     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
553
554     /* Create memory pool for application purpose */
555     pool = pj_pool_create( &cp.factory,     /* pool factory         */
556                            "app",           /* pool name.           */
557                            4000,            /* init size            */
558                            4000,            /* increment size       */
559                            NULL             /* callback on error    */
560                            );
561
562     /* Init video format manager */
563     pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
564
565     /* Init video converter manager */
566     pjmedia_converter_mgr_create(pool, NULL);
567
568     /* Init event manager */
569     pjmedia_event_mgr_create(pool, 0, NULL);
570
571     /* Init video codec manager */
572     pjmedia_vid_codec_mgr_create(pool, NULL);
573
574     /* Init video subsystem */
575     pjmedia_vid_dev_subsys_init(&cp.factory);
576
577     /* Register all supported codecs */
578     status = init_codecs(&cp.factory);
579     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
580
581
582     /* Find which codec to use. */
583     if (codec_id) {
584         unsigned count = 1;
585         pj_str_t str_codec_id = pj_str(codec_id);
586
587         status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL,
588                                                          &str_codec_id, &count,
589                                                          &codec_info, NULL);
590         if (status != PJ_SUCCESS) {
591             printf("Error: unable to find codec %s\n", codec_id);
592             return 1;
593         }
594     } else {
595         static pjmedia_vid_codec_info info[1];
596         unsigned count = PJ_ARRAY_SIZE(info);
597
598         /* Default to first codec */
599         pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, NULL);
600         codec_info = &info[0];
601     }
602
603     /* Get codec default param for info */
604     status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, 
605                                                      &codec_param);
606     pj_assert(status == PJ_SUCCESS);
607     
608     /* Set outgoing video size */
609     if (tx_size.w && tx_size.h)
610         codec_param.enc_fmt.det.vid.size = tx_size;
611
612 #if DEF_RENDERER_WIDTH && DEF_RENDERER_HEIGHT
613     /* Set incoming video size */
614     if (DEF_RENDERER_WIDTH > codec_param.dec_fmt.det.vid.size.w)
615         codec_param.dec_fmt.det.vid.size.w = DEF_RENDERER_WIDTH;
616     if (DEF_RENDERER_HEIGHT > codec_param.dec_fmt.det.vid.size.h)
617         codec_param.dec_fmt.det.vid.size.h = DEF_RENDERER_HEIGHT;
618 #endif
619
620     if (play_file.file_name) {
621         pjmedia_video_format_detail *file_vfd;
622         pjmedia_clock_param clock_param;
623         char fmt_name[5];
624
625         /* Create file player */
626         status = create_file_player(pool, play_file.file_name, &play_port);
627         if (status != PJ_SUCCESS)
628             goto on_exit;
629
630         /* Collect format info */
631         file_vfd = pjmedia_format_get_video_format_detail(&play_port->info.fmt,
632                                                           PJ_TRUE);
633         PJ_LOG(2, (THIS_FILE, "Reading video stream %dx%d %s @%.2ffps",
634                    file_vfd->size.w, file_vfd->size.h,
635                    pjmedia_fourcc_name(play_port->info.fmt.id, fmt_name),
636                    (1.0*file_vfd->fps.num/file_vfd->fps.denum)));
637
638         /* Allocate file read buffer */
639         play_file.read_buf_size = PJMEDIA_MAX_VIDEO_ENC_FRAME_SIZE;
640         play_file.read_buf = pj_pool_zalloc(pool, play_file.read_buf_size);
641
642         /* Create decoder, if the file and the stream uses different codec */
643         if (codec_info->fmt_id != (pjmedia_format_id)play_port->info.fmt.id) {
644             const pjmedia_video_format_info *dec_vfi;
645             pjmedia_video_apply_fmt_param dec_vafp = {0};
646             const pjmedia_vid_codec_info *codec_info2;
647             pjmedia_vid_codec_param codec_param2;
648
649             /* Find decoder */
650             status = pjmedia_vid_codec_mgr_get_codec_info2(NULL,
651                                                            play_port->info.fmt.id,
652                                                            &codec_info2);
653             if (status != PJ_SUCCESS)
654                 goto on_exit;
655
656             /* Init decoder */
657             status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info2,
658                                                        &play_decoder);
659             if (status != PJ_SUCCESS)
660                 goto on_exit;
661
662             status = play_decoder->op->init(play_decoder, pool);
663             if (status != PJ_SUCCESS)
664                 goto on_exit;
665
666             /* Open decoder */
667             status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info2,
668                                                              &codec_param2);
669             if (status != PJ_SUCCESS)
670                 goto on_exit;
671
672             codec_param2.dir = PJMEDIA_DIR_DECODING;
673             status = play_decoder->op->open(play_decoder, &codec_param2);
674             if (status != PJ_SUCCESS)
675                 goto on_exit;
676
677             /* Get decoder format info and apply param */
678             dec_vfi = pjmedia_get_video_format_info(NULL,
679                                                     codec_info2->dec_fmt_id[0]);
680             if (!dec_vfi || !dec_vfi->apply_fmt) {
681                 status = PJ_ENOTSUP;
682                 goto on_exit;
683             }
684             dec_vafp.size = file_vfd->size;
685             (*dec_vfi->apply_fmt)(dec_vfi, &dec_vafp);
686
687             /* Allocate buffer to receive decoder output */
688             play_file.dec_buf_size = dec_vafp.framebytes;
689             play_file.dec_buf = pj_pool_zalloc(pool, play_file.dec_buf_size);
690         }
691
692         /* Create player clock */
693         clock_param.usec_interval = PJMEDIA_PTIME(&file_vfd->fps);
694         clock_param.clock_rate = codec_info->clock_rate;
695         status = pjmedia_clock_create2(pool, &clock_param,
696                                        PJMEDIA_CLOCK_NO_HIGHEST_PRIO,
697                                        &clock_cb, &play_file, &play_clock);
698         if (status != PJ_SUCCESS)
699             goto on_exit;
700
701         /* Override stream codec param for encoding direction */
702         codec_param.enc_fmt.det.vid.size = file_vfd->size;
703         codec_param.enc_fmt.det.vid.fps  = file_vfd->fps;
704
705     } else {
706         pjmedia_vid_port_param_default(&vpp);
707
708         /* Set as active for all video devices */
709         vpp.active = PJ_TRUE;
710
711         /* Create video device port. */
712         if (dir & PJMEDIA_DIR_ENCODING) {
713             /* Create capture */
714             status = pjmedia_vid_dev_default_param(
715                                         pool,
716                                         PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
717                                         &vpp.vidparam);
718             if (status != PJ_SUCCESS)
719                 goto on_exit;
720
721             pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.enc_fmt);
722             vpp.vidparam.fmt.id = codec_param.dec_fmt.id;
723             vpp.vidparam.dir = PJMEDIA_DIR_CAPTURE;
724             
725             status = pjmedia_vid_port_create(pool, &vpp, &capture);
726             if (status != PJ_SUCCESS)
727                 goto on_exit;
728         }
729         
730         if (dir & PJMEDIA_DIR_DECODING) {
731             /* Create renderer */
732             status = pjmedia_vid_dev_default_param(
733                                         pool,
734                                         PJMEDIA_VID_DEFAULT_RENDER_DEV,
735                                         &vpp.vidparam);
736             if (status != PJ_SUCCESS)
737                 goto on_exit;
738
739             pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt);
740             vpp.vidparam.dir = PJMEDIA_DIR_RENDER;
741             vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size;
742             vpp.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
743             vpp.vidparam.window_flags = PJMEDIA_VID_DEV_WND_BORDER |
744                                         PJMEDIA_VID_DEV_WND_RESIZABLE;
745
746             status = pjmedia_vid_port_create(pool, &vpp, &renderer);
747             if (status != PJ_SUCCESS)
748                 goto on_exit;
749         }
750     }
751
752     /* Set to ignore fmtp */
753     codec_param.ignore_fmtp = PJ_TRUE;
754
755     /* Create stream based on program arguments */
756     status = create_stream(pool, med_endpt, codec_info, &codec_param,
757                            dir, rx_pt, tx_pt, local_port, &remote_addr, 
758 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
759                            use_srtp, &srtp_crypto_suite, 
760                            &srtp_tx_key, &srtp_rx_key,
761 #endif
762                            &stream);
763     if (status != PJ_SUCCESS)
764         goto on_exit;
765
766     /* Get the port interface of the stream */
767     status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_ENCODING,
768                                          &enc_port);
769     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
770
771     status = pjmedia_vid_stream_get_port(stream, PJMEDIA_DIR_DECODING,
772                                          &dec_port);
773     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
774
775     /* Start streaming */
776     status = pjmedia_vid_stream_start(stream);
777     if (status != PJ_SUCCESS)
778         goto on_exit;
779
780     /* Start renderer */
781     if (renderer) {
782         status = pjmedia_vid_port_connect(renderer, dec_port, PJ_FALSE);
783         if (status != PJ_SUCCESS)
784             goto on_exit;
785         status = pjmedia_vid_port_start(renderer);
786         if (status != PJ_SUCCESS)
787             goto on_exit;
788     }
789
790     /* Start capture */
791     if (capture) {
792         status = pjmedia_vid_port_connect(capture, enc_port, PJ_FALSE);
793         if (status != PJ_SUCCESS)
794             goto on_exit;
795         status = pjmedia_vid_port_start(capture);
796         if (status != PJ_SUCCESS)
797             goto on_exit;
798     }
799
800     /* Start playing file */
801     if (play_file.file_name) {
802
803 #if HAS_LOCAL_RENDERER_FOR_PLAY_FILE
804         /* Create local renderer */
805         pjmedia_vid_port_param_default(&vpp);
806         vpp.active = PJ_FALSE;
807         status = pjmedia_vid_dev_default_param(
808                                 pool,
809                                 PJMEDIA_VID_DEFAULT_RENDER_DEV,
810                                 &vpp.vidparam);
811         if (status != PJ_SUCCESS)
812             goto on_exit;
813
814         vpp.vidparam.dir = PJMEDIA_DIR_RENDER;
815         pjmedia_format_copy(&vpp.vidparam.fmt, &codec_param.dec_fmt);
816         vpp.vidparam.fmt.det.vid.size = play_port->info.fmt.det.vid.size;
817         vpp.vidparam.fmt.det.vid.fps = play_port->info.fmt.det.vid.fps;
818         vpp.vidparam.disp_size = vpp.vidparam.fmt.det.vid.size;
819         vpp.vidparam.flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS;
820         vpp.vidparam.window_flags = PJMEDIA_VID_DEV_WND_BORDER |
821                                     PJMEDIA_VID_DEV_WND_RESIZABLE;
822
823         status = pjmedia_vid_port_create(pool, &vpp, &renderer);
824         if (status != PJ_SUCCESS)
825             goto on_exit;
826         status = pjmedia_vid_port_start(renderer);
827         if (status != PJ_SUCCESS)
828             goto on_exit;
829 #endif
830
831         /* Init play file data */
832         play_file.play_port = play_port;
833         play_file.stream_port = enc_port;
834         play_file.decoder = play_decoder;
835         if (renderer) {
836             play_file.renderer = pjmedia_vid_port_get_passive_port(renderer);
837         }
838
839         status = pjmedia_clock_start(play_clock);
840         if (status != PJ_SUCCESS)
841             goto on_exit;
842     }
843
844     /* Done */
845
846     if (dir == PJMEDIA_DIR_DECODING)
847         printf("Stream is active, dir is recv-only, local port is %d\n",
848                local_port);
849     else if (dir == PJMEDIA_DIR_ENCODING)
850         printf("Stream is active, dir is send-only, sending to %s:%d\n",
851                pj_inet_ntoa(remote_addr.sin_addr),
852                pj_ntohs(remote_addr.sin_port));
853     else
854         printf("Stream is active, send/recv, local port is %d, "
855                "sending to %s:%d\n",
856                local_port,
857                pj_inet_ntoa(remote_addr.sin_addr),
858                pj_ntohs(remote_addr.sin_port));
859
860     if (dir & PJMEDIA_DIR_ENCODING)
861         PJ_LOG(2, (THIS_FILE, "Sending %dx%d %.*s @%.2ffps",
862                    codec_param.enc_fmt.det.vid.size.w,
863                    codec_param.enc_fmt.det.vid.size.h,
864                    codec_info->encoding_name.slen,
865                    codec_info->encoding_name.ptr,
866                    (1.0*codec_param.enc_fmt.det.vid.fps.num/
867                     codec_param.enc_fmt.det.vid.fps.denum)));
868
869     for (;;) {
870         char tmp[10];
871
872         puts("");
873         puts("Commands:");
874         puts("  q     Quit");
875         puts("");
876
877         printf("Command: "); fflush(stdout);
878
879         if (fgets(tmp, sizeof(tmp), stdin) == NULL) {
880             puts("EOF while reading stdin, will quit now..");
881             break;
882         }
883
884         if (tmp[0] == 'q')
885             break;
886
887     }
888
889
890
891     /* Start deinitialization: */
892 on_exit:
893
894     /* Stop video devices */
895     if (capture)
896         pjmedia_vid_port_stop(capture);
897     if (renderer)
898         pjmedia_vid_port_stop(renderer);
899
900     /* Stop and destroy file clock */
901     if (play_clock) {
902         pjmedia_clock_stop(play_clock);
903         pjmedia_clock_destroy(play_clock);
904     }
905
906     /* Destroy file reader/player */
907     if (play_port)
908         pjmedia_port_destroy(play_port);
909
910     /* Destroy file decoder */
911     if (play_decoder) {
912         play_decoder->op->close(play_decoder);
913         pjmedia_vid_codec_mgr_dealloc_codec(NULL, play_decoder);
914     }
915
916     /* Destroy video devices */
917     if (capture)
918         pjmedia_vid_port_destroy(capture);
919     if (renderer)
920         pjmedia_vid_port_destroy(renderer);
921
922     /* Destroy stream */
923     if (stream) {
924         pjmedia_transport *tp;
925
926         tp = pjmedia_vid_stream_get_transport(stream);
927         pjmedia_vid_stream_destroy(stream);
928         
929         pjmedia_transport_close(tp);
930     }
931
932     /* Deinit codecs */
933     deinit_codecs();
934
935     /* Shutdown video subsystem */
936     pjmedia_vid_dev_subsys_shutdown();
937
938     /* Destroy event manager */
939     pjmedia_event_mgr_destroy(NULL);
940
941     /* Release application pool */
942     pj_pool_release( pool );
943
944     /* Destroy media endpoint. */
945     pjmedia_endpt_destroy( med_endpt );
946
947     /* Destroy pool factory */
948     pj_caching_pool_destroy( &cp );
949
950     /* Shutdown PJLIB */
951     pj_shutdown();
952
953     return (status == PJ_SUCCESS) ? 0 : 1;
954 }
955
956
957 #else
958
959 int main(int argc, char *argv[])
960 {
961     PJ_UNUSED_ARG(argc);
962     PJ_UNUSED_ARG(argv);
963     puts("Error: this sample requires video capability (PJMEDIA_HAS_VIDEO == 1)");
964     return -1;
965 }
966
967 #endif /* PJMEDIA_HAS_VIDEO */