blob: bdeab3f95b5054261cfae0234da75283d565c89f [file] [log] [blame]
Sauw Mingb1b17d22010-12-20 11:02:48 +00001/* $Id$ */
2/*
3 * Copyright (C) 2010-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#include <pjmedia.h>
21#include <pjmedia/converter.h>
22#include <pjmedia-codec.h>
23#include <pjlib-util.h>
24#include <pjlib.h>
25#include <stdio.h>
26#include <stdlib.h>
27
28#include "util.h"
29
30/**
31 * \page page_pjmedia_samples_aviplay_c Samples: Playing AVI File to
32 * Video and Sound Devices
33 *
34 * This is a very simple example to use the @ref PJMEDIA_FILE_PLAY,
35 * @ref PJMED_SND_PORT, and @ref PJMEDIA_VID_PORT. In this example, we
36 * open the file, video, and sound devices, then connect the file to both
37 * video and sound devices to play the contents of the file.
38 *
39 *
40 * This file is pjsip-apps/src/samples/aviplay.c
41 *
42 * \includelineno aviplay.c
43 */
44
45
46/*
47 * aviplay.c
48 *
49 * PURPOSE:
50 * Play a AVI file to video and sound devices.
51 *
52 * USAGE:
53 * aviplay FILE.AVI
54 */
55
56
Nanang Izzuddin63b3c132011-07-19 11:11:07 +000057#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
58
59
Sauw Mingb1b17d22010-12-20 11:02:48 +000060/* For logging purpose. */
61#define THIS_FILE "aviplay.c"
62
Sauw Mingb1b17d22010-12-20 11:02:48 +000063static const char *desc =
64" FILE \n"
65" \n"
66" aviplay.c \n"
67" \n"
68" PURPOSE \n"
69" \n"
70" Demonstrate how to play a AVI file. \n"
71" \n"
72" USAGE \n"
73" \n"
74" aviplay FILE.AVI \n";
75
76struct codec_fmt {
77 pj_uint32_t pjmedia_id;
78 const char *codec_id;
79 /* Do we need to convert the decoded frame? */
80 pj_bool_t need_conversion;
81 /* If conversion is needed, dst_fmt indicates the destination format */
82 pjmedia_format_id dst_fmt;
83} codec_fmts[] = {{PJMEDIA_FORMAT_MJPEG, "mjpeg",
84 PJ_TRUE , PJMEDIA_FORMAT_I420},
85 {PJMEDIA_FORMAT_H263 , "h263" ,
86 PJ_FALSE, 0},
Nanang Izzuddin235e1b42011-02-28 18:59:47 +000087 {PJMEDIA_FORMAT_XVID , "xvid"},
Sauw Mingb1b17d22010-12-20 11:02:48 +000088 };
89
90typedef struct avi_port_t
91{
92 pjmedia_vid_port *vid_port;
Sauw Ming8e799122010-12-30 16:31:16 +000093 pjmedia_snd_port *snd_port;
Sauw Mingb1b17d22010-12-20 11:02:48 +000094 pj_bool_t is_running;
95 pj_bool_t is_quitting;
96} avi_port_t;
97
98typedef struct codec_port_data_t
99{
100 pjmedia_vid_codec *codec;
101 pjmedia_port *src_port;
102 pj_uint8_t *enc_buf;
103 pj_size_t enc_buf_size;
104
105 pjmedia_converter *conv;
106} codec_port_data_t;
107
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000108static pj_status_t avi_event_cb(pjmedia_event *event,
109 void *user_data)
Sauw Mingb1b17d22010-12-20 11:02:48 +0000110{
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000111 avi_port_t *ap = (avi_port_t *)user_data;
Sauw Mingb1b17d22010-12-20 11:02:48 +0000112
Benny Prijono934af0f2011-07-12 03:05:35 +0000113 switch (event->type) {
114 case PJMEDIA_EVENT_WND_CLOSED:
Sauw Mingb1b17d22010-12-20 11:02:48 +0000115 ap->is_quitting = PJ_TRUE;
116 break;
Benny Prijono934af0f2011-07-12 03:05:35 +0000117 case PJMEDIA_EVENT_MOUSE_BTN_DOWN:
Sauw Mingb1b17d22010-12-20 11:02:48 +0000118 if (ap->is_running) {
119 pjmedia_vid_port_stop(ap->vid_port);
Sauw Ming8e799122010-12-30 16:31:16 +0000120 if (ap->snd_port)
121 pjmedia_aud_stream_stop(
122 pjmedia_snd_port_get_snd_stream(ap->snd_port));
Sauw Mingb1b17d22010-12-20 11:02:48 +0000123 } else {
124 pjmedia_vid_port_start(ap->vid_port);
Sauw Ming8e799122010-12-30 16:31:16 +0000125 if (ap->snd_port)
126 pjmedia_aud_stream_start(
127 pjmedia_snd_port_get_snd_stream(ap->snd_port));
Sauw Mingb1b17d22010-12-20 11:02:48 +0000128 }
129 ap->is_running = !ap->is_running;
130 break;
131 default:
132 return PJ_SUCCESS;
133 }
134
135 /* We handled the event on our own, so return non-PJ_SUCCESS here */
136 return -1;
137}
138
139static pj_status_t codec_get_frame(pjmedia_port *port,
140 pjmedia_frame *frame)
141{
142 codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
143 pjmedia_vid_codec *codec = port_data->codec;
144 pjmedia_frame enc_frame;
145 pj_status_t status;
146
147 enc_frame.buf = port_data->enc_buf;
148 enc_frame.size = port_data->enc_buf_size;
149
150 if (port_data->conv) {
151 pj_size_t frame_size = frame->size;
152
153 status = pjmedia_port_get_frame(port_data->src_port, frame);
154 if (status != PJ_SUCCESS) goto on_error;
155
Benny Prijono84685572011-09-29 08:31:15 +0000156 status = pjmedia_vid_codec_decode(codec, 1, frame,
157 frame->size, &enc_frame);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000158 if (status != PJ_SUCCESS) goto on_error;
159
160 frame->size = frame_size;
161 status = pjmedia_converter_convert(port_data->conv, &enc_frame, frame);
162 if (status != PJ_SUCCESS) goto on_error;
163
164 return PJ_SUCCESS;
165 }
166
167 status = pjmedia_port_get_frame(port_data->src_port, &enc_frame);
168 if (status != PJ_SUCCESS) goto on_error;
169
Benny Prijono84685572011-09-29 08:31:15 +0000170 status = pjmedia_vid_codec_decode(codec, 1, &enc_frame,
171 frame->size, frame);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000172 if (status != PJ_SUCCESS) goto on_error;
173
174 return PJ_SUCCESS;
175
176on_error:
177 pj_perror(3, THIS_FILE, status, "codec_get_frame() error");
178 return status;
179}
180
181static int aviplay(pj_pool_t *pool, const char *fname)
182{
183 pjmedia_vid_port *renderer=NULL;
184 pjmedia_vid_port_param param;
Sauw Mingb1b17d22010-12-20 11:02:48 +0000185 const pjmedia_video_format_info *vfi;
186 pjmedia_video_format_detail *vfd;
Sauw Ming8e799122010-12-30 16:31:16 +0000187 pjmedia_snd_port *snd_port = NULL;
Sauw Mingb1b17d22010-12-20 11:02:48 +0000188 pj_status_t status;
189 int rc = 0;
190 pjmedia_avi_streams *avi_streams;
191 pjmedia_avi_stream *vid_stream, *aud_stream;
192 pjmedia_port *vid_port = NULL, *aud_port = NULL;
193 pjmedia_vid_codec *codec=NULL;
194 avi_port_t avi_port;
195
196 pj_bzero(&avi_port, sizeof(avi_port));
197
198 status = pjmedia_avi_player_create_streams(pool, fname, 0, &avi_streams);
199 if (status != PJ_SUCCESS) {
200 PJ_PERROR(2,("", status, " Error playing %s", fname));
201 rc = 210; goto on_return;
202 }
203
204 vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
205 0,
206 PJMEDIA_TYPE_VIDEO);
207 vid_port = pjmedia_avi_stream_get_port(vid_stream);
208
209 if (vid_port) {
210 pjmedia_vid_port_param_default(&param);
211
212 status = pjmedia_vid_dev_default_param(pool,
213 PJMEDIA_VID_DEFAULT_RENDER_DEV,
214 &param.vidparam);
215 if (status != PJ_SUCCESS) {
216 rc = 220; goto on_return;
217 }
218
219 /* Create renderer, set it to active */
220 param.active = PJ_TRUE;
221 param.vidparam.dir = PJMEDIA_DIR_RENDER;
222 vfd = pjmedia_format_get_video_format_detail(&vid_port->info.fmt,
223 PJ_TRUE);
224 pjmedia_format_init_video(&param.vidparam.fmt,
225 vid_port->info.fmt.id,
226 vfd->size.w, vfd->size.h,
227 vfd->fps.num, vfd->fps.denum);
228
229 vfi = pjmedia_get_video_format_info(
230 pjmedia_video_format_mgr_instance(),
231 vid_port->info.fmt.id);
232 /* Check whether the frame is encoded */
233 if (!vfi || vfi->bpp == 0) {
234 /* Yes, prepare codec */
235 pj_str_t codec_id_st;
236 unsigned info_cnt = 1, i, k;
237 const pjmedia_vid_codec_info *codec_info;
238 pj_str_t port_name = {"codec", 5};
239 pj_uint8_t *enc_buf = NULL;
240 pj_size_t enc_buf_size = 0;
241 pjmedia_vid_dev_info rdr_info;
242 pjmedia_port codec_port;
243 codec_port_data_t codec_port_data;
244 pjmedia_vid_codec_param codec_param;
245 struct codec_fmt *codecp = NULL;
246
247 /* Lookup codec */
248 for (i = 0; i < sizeof(codec_fmts)/sizeof(codec_fmts[0]); i++) {
249 if (vid_port->info.fmt.id == codec_fmts[i].pjmedia_id) {
250 codecp = &codec_fmts[i];
251 break;
252 }
253 }
254 if (!codecp) {
255 rc = 242; goto on_return;
256 }
257 pj_cstr(&codec_id_st, codecp->codec_id);
258 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL,
259 &codec_id_st,
260 &info_cnt,
261 &codec_info,
262 NULL);
263 if (status != PJ_SUCCESS) {
264 rc = 245; goto on_return;
265 }
266 status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
267 &codec_param);
268 if (status != PJ_SUCCESS) {
269 rc = 246; goto on_return;
270 }
271
272 pjmedia_vid_dev_get_info(param.vidparam.rend_id, &rdr_info);
273 for (i=0; i<codec_info->dec_fmt_id_cnt; ++i) {
274 for (k=0; k<rdr_info.fmt_cnt; ++k) {
275 if (codec_info->dec_fmt_id[i]==(int)rdr_info.fmt[k].id)
276 {
277 param.vidparam.fmt.id = codec_info->dec_fmt_id[i];
278 }
279 }
280 }
281
282 /* Open codec */
283 status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
284 &codec);
285 if (status != PJ_SUCCESS) {
286 rc = 250; goto on_return;
287 }
288
Nanang Izzuddin96f8dca2011-07-15 10:19:59 +0000289 status = pjmedia_vid_codec_init(codec, pool);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000290 if (status != PJ_SUCCESS) {
291 rc = 251; goto on_return;
292 }
293
294 pjmedia_format_copy(&codec_param.dec_fmt, &param.vidparam.fmt);
Benny Prijono84685572011-09-29 08:31:15 +0000295 codec_param.packing = PJMEDIA_VID_PACKING_WHOLE;
Nanang Izzuddin96f8dca2011-07-15 10:19:59 +0000296 status = pjmedia_vid_codec_open(codec, &codec_param);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000297 if (status != PJ_SUCCESS) {
298 rc = 252; goto on_return;
299 }
300
301 /* Alloc encoding buffer */
302 enc_buf_size = codec_param.dec_fmt.det.vid.size.w *
303 codec_param.dec_fmt.det.vid.size.h * 4
304 + 16; /*< padding, just in case */
305 enc_buf = pj_pool_alloc(pool,enc_buf_size);
306
307 /* Init codec port */
308 pj_bzero(&codec_port, sizeof(codec_port));
309 status = pjmedia_port_info_init2(&codec_port.info, &port_name,
310 0x1234,
311 PJMEDIA_DIR_ENCODING,
312 &codec_param.dec_fmt);
313 if (status != PJ_SUCCESS) {
314 rc = 260; goto on_return;
315 }
316 pj_bzero(&codec_port_data, sizeof(codec_port_data));
317 codec_port_data.codec = codec;
318 codec_port_data.src_port = vid_port;
319 codec_port_data.enc_buf = enc_buf;
320 codec_port_data.enc_buf_size = enc_buf_size;
321
322 codec_port.get_frame = &codec_get_frame;
323 codec_port.port_data.pdata = &codec_port_data;
324
325 /* Check whether we need to convert the decoded frame */
326 if (codecp->need_conversion) {
327 pjmedia_conversion_param conv_param;
328
Sauw Mingb1b17d22010-12-20 11:02:48 +0000329 pjmedia_format_copy(&conv_param.src, &param.vidparam.fmt);
330 pjmedia_format_copy(&conv_param.dst, &param.vidparam.fmt);
331 conv_param.dst.id = codecp->dst_fmt;
332 param.vidparam.fmt.id = conv_param.dst.id;
333
334 status = pjmedia_converter_create(NULL, pool, &conv_param,
335 &codec_port_data.conv);
336 if (status != PJ_SUCCESS) {
337 rc = 270; goto on_return;
338 }
339 }
340
341 status = pjmedia_vid_port_create(pool, &param, &renderer);
342 if (status != PJ_SUCCESS) {
343 rc = 230; goto on_return;
344 }
345
346 status = pjmedia_vid_port_connect(renderer, &codec_port,
347 PJ_FALSE);
348 } else {
349 status = pjmedia_vid_port_create(pool, &param, &renderer);
350 if (status != PJ_SUCCESS) {
351 rc = 230; goto on_return;
352 }
353
354 /* Connect avi port to renderer */
355 status = pjmedia_vid_port_connect(renderer, vid_port,
356 PJ_FALSE);
357 }
358
359 if (status != PJ_SUCCESS) {
360 rc = 240; goto on_return;
361 }
362 }
363
364 aud_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
365 0,
366 PJMEDIA_TYPE_AUDIO);
367 aud_port = pjmedia_avi_stream_get_port(aud_stream);
368
369 if (aud_port) {
Sauw Ming8e799122010-12-30 16:31:16 +0000370 /* Create sound player port. */
371 status = pjmedia_snd_port_create_player(
372 pool, /* pool */
373 -1, /* use default dev. */
374 PJMEDIA_PIA_SRATE(&aud_port->info),/* clock rate. */
375 PJMEDIA_PIA_CCNT(&aud_port->info), /* # of channels. */
376 PJMEDIA_PIA_SPF(&aud_port->info), /* samples per frame. */
377 PJMEDIA_PIA_BITS(&aud_port->info), /* bits per sample. */
378 0, /* options */
379 &snd_port /* returned port */
380 );
Sauw Mingb1b17d22010-12-20 11:02:48 +0000381 if (status != PJ_SUCCESS) {
382 rc = 310; goto on_return;
383 }
384
Sauw Ming8e799122010-12-30 16:31:16 +0000385 /* Connect file port to the sound player.
386 * Stream playing will commence immediately.
387 */
388 status = pjmedia_snd_port_connect(snd_port, aud_port);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000389 if (status != PJ_SUCCESS) {
390 rc = 330; goto on_return;
391 }
392 }
393
394 if (vid_port) {
Sauw Ming5291a2d2011-07-15 07:52:44 +0000395 pjmedia_vid_dev_cb cb;
Sauw Mingb1b17d22010-12-20 11:02:48 +0000396
397 pj_bzero(&cb, sizeof(cb));
Sauw Ming8e799122010-12-30 16:31:16 +0000398 avi_port.snd_port = snd_port;
Sauw Mingb1b17d22010-12-20 11:02:48 +0000399 avi_port.vid_port = renderer;
400 avi_port.is_running = PJ_TRUE;
401 pjmedia_vid_port_set_cb(renderer, &cb, &avi_port);
Sauw Ming8e799122010-12-30 16:31:16 +0000402
Benny Prijono934af0f2011-07-12 03:05:35 +0000403 /* subscribe events */
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000404 pjmedia_event_subscribe(NULL, pool, &avi_event_cb, &avi_port,
405 renderer);
Benny Prijono934af0f2011-07-12 03:05:35 +0000406
Sauw Ming8e799122010-12-30 16:31:16 +0000407 if (snd_port) {
408 /* Synchronize video rendering and audio playback */
409 pjmedia_vid_port_set_clock_src(
Sauw Ming8985f552011-07-13 02:37:14 +0000410 renderer,
Sauw Ming8e799122010-12-30 16:31:16 +0000411 pjmedia_snd_port_get_clock_src(
412 snd_port, PJMEDIA_DIR_PLAYBACK));
413 }
414
Sauw Mingb1b17d22010-12-20 11:02:48 +0000415
416 /* Start video streaming.. */
417 status = pjmedia_vid_port_start(renderer);
418 if (status != PJ_SUCCESS) {
419 rc = 270; goto on_return;
420 }
421 }
422
423 while (!avi_port.is_quitting) {
424 pj_thread_sleep(100);
425 }
426
427on_return:
Sauw Ming8e799122010-12-30 16:31:16 +0000428 if (snd_port) {
429 pjmedia_snd_port_disconnect(snd_port);
430 /* Without this sleep, Windows/DirectSound will repeteadly
431 * play the last frame during destroy.
432 */
433 pj_thread_sleep(100);
434 pjmedia_snd_port_destroy(snd_port);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000435 }
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000436 if (renderer) {
437 pjmedia_event_unsubscribe(NULL, &avi_event_cb, &avi_port,
438 renderer);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000439 pjmedia_vid_port_destroy(renderer);
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000440 }
Sauw Ming8e799122010-12-30 16:31:16 +0000441 if (aud_port)
442 pjmedia_port_destroy(aud_port);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000443 if (vid_port)
444 pjmedia_port_destroy(vid_port);
445 if (codec) {
Nanang Izzuddin96f8dca2011-07-15 10:19:59 +0000446 pjmedia_vid_codec_close(codec);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000447 pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec);
448 }
449
450 return rc;
451}
452
453
Sauw Ming01095d82011-07-14 08:46:19 +0000454static int main_func(int argc, char *argv[])
Sauw Mingb1b17d22010-12-20 11:02:48 +0000455{
456 pj_caching_pool cp;
457 pj_pool_t *pool;
458 int rc = 0;
459 pj_status_t status = PJ_SUCCESS;
460
461 if (argc != 2) {
462 puts("Error: filename required");
463 puts(desc);
464 return 1;
465 }
466
467
468 /* Must init PJLIB first: */
469 status = pj_init();
470 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
471
472 /* Must create a pool factory before we can allocate any memory. */
473 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
474
475 /* Create memory pool for our file player */
476 pool = pj_pool_create( &cp.factory, /* pool factory */
477 "AVI", /* pool name. */
478 4000, /* init size */
479 4000, /* increment size */
480 NULL /* callback on error */
481 );
482
483 pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
484 pjmedia_converter_mgr_create(pool, NULL);
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000485 pjmedia_event_mgr_create(pool, 0, NULL);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000486 pjmedia_vid_codec_mgr_create(pool, NULL);
487
Sauw Minge90ece82011-06-09 04:05:44 +0000488 status = pjmedia_vid_dev_subsys_init(&cp.factory);
Sauw Mingb1b17d22010-12-20 11:02:48 +0000489 if (status != PJ_SUCCESS)
490 goto on_return;
491
492 status = pjmedia_aud_subsys_init(&cp.factory);
493 if (status != PJ_SUCCESS) {
494 goto on_return;
495 }
496
Benny Prijono4ba7c952011-08-08 04:18:55 +0000497#if PJMEDIA_HAS_FFMPEG_CODEC
Sauw Mingb1b17d22010-12-20 11:02:48 +0000498 status = pjmedia_codec_ffmpeg_init(NULL, &cp.factory);
499 if (status != PJ_SUCCESS)
500 goto on_return;
Benny Prijono4ba7c952011-08-08 04:18:55 +0000501#endif
Sauw Mingb1b17d22010-12-20 11:02:48 +0000502
503 rc = aviplay(pool, argv[1]);
504
505 /*
506 * File should be playing and looping now
507 */
508
509 /* Without this sleep, Windows/DirectSound will repeteadly
510 * play the last frame during destroy.
511 */
512 pj_thread_sleep(100);
513
514on_return:
Benny Prijono4ba7c952011-08-08 04:18:55 +0000515#if PJMEDIA_HAS_FFMPEG_CODEC
Sauw Mingb1b17d22010-12-20 11:02:48 +0000516 pjmedia_codec_ffmpeg_deinit();
Benny Prijono4ba7c952011-08-08 04:18:55 +0000517#endif
Sauw Mingb1b17d22010-12-20 11:02:48 +0000518 pjmedia_aud_subsys_shutdown();
Sauw Minge90ece82011-06-09 04:05:44 +0000519 pjmedia_vid_dev_subsys_shutdown();
Sauw Mingb1b17d22010-12-20 11:02:48 +0000520
521 pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr_instance());
522 pjmedia_converter_mgr_destroy(pjmedia_converter_mgr_instance());
Sauw Ming0fabe1b2011-12-01 10:49:07 +0000523 pjmedia_event_mgr_destroy(pjmedia_event_mgr_instance());
Sauw Mingb1b17d22010-12-20 11:02:48 +0000524 pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr_instance());
525
526 /* Release application pool */
527 pj_pool_release( pool );
528
529 /* Destroy pool factory */
530 pj_caching_pool_destroy( &cp );
531
532 /* Shutdown PJLIB */
533 pj_shutdown();
534
535 /* Done. */
536 return 0;
537}
Sauw Ming01095d82011-07-14 08:46:19 +0000538
539int main(int argc, char *argv[])
540{
541 return pj_run_app(&main_func, argc, argv, 0);
542}
Nanang Izzuddin63b3c132011-07-19 11:11:07 +0000543
544#else
545
546int main(int argc, char *argv[])
547{
548 PJ_UNUSED_ARG(argc);
549 PJ_UNUSED_ARG(argv);
550 puts("Error: this sample requires video capability (PJMEDIA_HAS_VIDEO == 1)");
551 return -1;
552}
553
554#endif /* PJMEDIA_HAS_VIDEO */