blob: 35b050443a5116914468ee45ef72fbbd967a8ea7 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $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
57#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
58
59
60/* For logging purpose. */
61#define THIS_FILE "aviplay.c"
62
63static 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},
87 {PJMEDIA_FORMAT_MPEG4, "mp4v"},
88 {PJMEDIA_FORMAT_H264 , "h264"}
89 };
90
91typedef struct avi_port_t
92{
93 pjmedia_vid_port *vid_port;
94 pjmedia_snd_port *snd_port;
95 pj_bool_t is_running;
96 pj_bool_t is_quitting;
97} avi_port_t;
98
99typedef struct codec_port_data_t
100{
101 pjmedia_vid_codec *codec;
102 pjmedia_port *src_port;
103 pj_uint8_t *enc_buf;
104 pj_size_t enc_buf_size;
105
106 pjmedia_converter *conv;
107} codec_port_data_t;
108
109static pj_status_t avi_event_cb(pjmedia_event *event,
110 void *user_data)
111{
112 avi_port_t *ap = (avi_port_t *)user_data;
113
114 switch (event->type) {
115 case PJMEDIA_EVENT_WND_CLOSED:
116 ap->is_quitting = PJ_TRUE;
117 break;
118 case PJMEDIA_EVENT_MOUSE_BTN_DOWN:
119 if (ap->is_running) {
120 pjmedia_vid_port_stop(ap->vid_port);
121 if (ap->snd_port)
122 pjmedia_aud_stream_stop(
123 pjmedia_snd_port_get_snd_stream(ap->snd_port));
124 } else {
125 pjmedia_vid_port_start(ap->vid_port);
126 if (ap->snd_port)
127 pjmedia_aud_stream_start(
128 pjmedia_snd_port_get_snd_stream(ap->snd_port));
129 }
130 ap->is_running = !ap->is_running;
131 break;
132 default:
133 return PJ_SUCCESS;
134 }
135
136 /* We handled the event on our own, so return non-PJ_SUCCESS here */
137 return -1;
138}
139
140static pj_status_t codec_get_frame(pjmedia_port *port,
141 pjmedia_frame *frame)
142{
143 codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata;
144 pjmedia_vid_codec *codec = port_data->codec;
145 pjmedia_frame enc_frame;
146 pj_status_t status;
147
148 enc_frame.buf = port_data->enc_buf;
149 enc_frame.size = port_data->enc_buf_size;
150
151 if (port_data->conv) {
152 pj_size_t frame_size = frame->size;
153
154 status = pjmedia_port_get_frame(port_data->src_port, frame);
155 if (status != PJ_SUCCESS) goto on_error;
156
157 status = pjmedia_vid_codec_decode(codec, 1, frame,
158 (unsigned)frame->size, &enc_frame);
159 if (status != PJ_SUCCESS) goto on_error;
160
161 frame->size = frame_size;
162 status = pjmedia_converter_convert(port_data->conv, &enc_frame, frame);
163 if (status != PJ_SUCCESS) goto on_error;
164
165 return PJ_SUCCESS;
166 }
167
168 status = pjmedia_port_get_frame(port_data->src_port, &enc_frame);
169 if (status != PJ_SUCCESS) goto on_error;
170
171 status = pjmedia_vid_codec_decode(codec, 1, &enc_frame,
172 (unsigned)frame->size, frame);
173 if (status != PJ_SUCCESS) goto on_error;
174
175 return PJ_SUCCESS;
176
177on_error:
178 pj_perror(3, THIS_FILE, status, "codec_get_frame() error");
179 return status;
180}
181
182static int aviplay(pj_pool_t *pool, const char *fname)
183{
184 pjmedia_vid_port *renderer=NULL;
185 pjmedia_vid_port_param param;
186 const pjmedia_video_format_info *vfi;
187 pjmedia_video_format_detail *vfd;
188 pjmedia_snd_port *snd_port = NULL;
189 pj_status_t status;
190 int rc = 0;
191 pjmedia_avi_streams *avi_streams;
192 pjmedia_avi_stream *vid_stream, *aud_stream;
193 pjmedia_port *vid_port = NULL, *aud_port = NULL;
194 pjmedia_vid_codec *codec=NULL;
195 avi_port_t avi_port;
196
197 pj_bzero(&avi_port, sizeof(avi_port));
198
199 status = pjmedia_avi_player_create_streams(pool, fname, 0, &avi_streams);
200 if (status != PJ_SUCCESS) {
201 PJ_PERROR(2,("", status, " Error playing %s", fname));
202 rc = 210; goto on_return;
203 }
204
205 vid_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
206 0,
207 PJMEDIA_TYPE_VIDEO);
208 vid_port = pjmedia_avi_stream_get_port(vid_stream);
209
210 if (vid_port) {
211 pjmedia_vid_port_param_default(&param);
212
213 status = pjmedia_vid_dev_default_param(pool,
214 PJMEDIA_VID_DEFAULT_RENDER_DEV,
215 &param.vidparam);
216 if (status != PJ_SUCCESS) {
217 rc = 220; goto on_return;
218 }
219
220 /* Create renderer, set it to active */
221 param.active = PJ_TRUE;
222 param.vidparam.dir = PJMEDIA_DIR_RENDER;
223 vfd = pjmedia_format_get_video_format_detail(&vid_port->info.fmt,
224 PJ_TRUE);
225 pjmedia_format_init_video(&param.vidparam.fmt,
226 vid_port->info.fmt.id,
227 vfd->size.w, vfd->size.h,
228 vfd->fps.num, vfd->fps.denum);
229
230 vfi = pjmedia_get_video_format_info(
231 pjmedia_video_format_mgr_instance(),
232 vid_port->info.fmt.id);
233 /* Check whether the frame is encoded */
234 if (!vfi || vfi->bpp == 0) {
235 /* Yes, prepare codec */
236 pj_str_t codec_id_st;
237 unsigned info_cnt = 1, i, k;
238 const pjmedia_vid_codec_info *codec_info;
239 pj_str_t port_name = {"codec", 5};
240 pj_uint8_t *enc_buf = NULL;
241 pj_size_t enc_buf_size = 0;
242 pjmedia_vid_dev_info rdr_info;
243 pjmedia_port codec_port;
244 codec_port_data_t codec_port_data;
245 pjmedia_vid_codec_param codec_param;
246 struct codec_fmt *codecp = NULL;
247
248 /* Lookup codec */
249 for (i = 0; i < sizeof(codec_fmts)/sizeof(codec_fmts[0]); i++) {
250 if (vid_port->info.fmt.id == codec_fmts[i].pjmedia_id) {
251 codecp = &codec_fmts[i];
252 break;
253 }
254 }
255 if (!codecp) {
256 rc = 242; goto on_return;
257 }
258 pj_cstr(&codec_id_st, codecp->codec_id);
259 status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL,
260 &codec_id_st,
261 &info_cnt,
262 &codec_info,
263 NULL);
264 if (status != PJ_SUCCESS) {
265 rc = 245; goto on_return;
266 }
267 status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info,
268 &codec_param);
269 if (status != PJ_SUCCESS) {
270 rc = 246; goto on_return;
271 }
272
273 pjmedia_format_copy(&codec_param.enc_fmt, &param.vidparam.fmt);
274
275 pjmedia_vid_dev_get_info(param.vidparam.rend_id, &rdr_info);
276 for (i=0; i<codec_info->dec_fmt_id_cnt; ++i) {
277 for (k=0; k<rdr_info.fmt_cnt; ++k) {
278 if (codec_info->dec_fmt_id[i]==(int)rdr_info.fmt[k].id)
279 {
280 param.vidparam.fmt.id = codec_info->dec_fmt_id[i];
281 i = codec_info->dec_fmt_id_cnt;
282 break;
283 }
284 }
285 }
286
287 /* Open codec */
288 status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info,
289 &codec);
290 if (status != PJ_SUCCESS) {
291 rc = 250; goto on_return;
292 }
293
294 status = pjmedia_vid_codec_init(codec, pool);
295 if (status != PJ_SUCCESS) {
296 rc = 251; goto on_return;
297 }
298
299 pjmedia_format_copy(&codec_param.dec_fmt, &param.vidparam.fmt);
300 codec_param.dir = PJMEDIA_DIR_DECODING;
301 codec_param.packing = PJMEDIA_VID_PACKING_WHOLE;
302 status = pjmedia_vid_codec_open(codec, &codec_param);
303 if (status != PJ_SUCCESS) {
304 rc = 252; goto on_return;
305 }
306
307 /* Alloc encoding buffer */
308 enc_buf_size = codec_param.dec_fmt.det.vid.size.w *
309 codec_param.dec_fmt.det.vid.size.h * 4
310 + 16; /*< padding, just in case */
311 enc_buf = pj_pool_alloc(pool,enc_buf_size);
312
313 /* Init codec port */
314 pj_bzero(&codec_port, sizeof(codec_port));
315 status = pjmedia_port_info_init2(&codec_port.info, &port_name,
316 0x1234,
317 PJMEDIA_DIR_ENCODING,
318 &codec_param.dec_fmt);
319 if (status != PJ_SUCCESS) {
320 rc = 260; goto on_return;
321 }
322 pj_bzero(&codec_port_data, sizeof(codec_port_data));
323 codec_port_data.codec = codec;
324 codec_port_data.src_port = vid_port;
325 codec_port_data.enc_buf = enc_buf;
326 codec_port_data.enc_buf_size = enc_buf_size;
327
328 codec_port.get_frame = &codec_get_frame;
329 codec_port.port_data.pdata = &codec_port_data;
330
331 /* Check whether we need to convert the decoded frame */
332 if (codecp->need_conversion) {
333 pjmedia_conversion_param conv_param;
334
335 pjmedia_format_copy(&conv_param.src, &param.vidparam.fmt);
336 pjmedia_format_copy(&conv_param.dst, &param.vidparam.fmt);
337 conv_param.dst.id = codecp->dst_fmt;
338 param.vidparam.fmt.id = conv_param.dst.id;
339
340 status = pjmedia_converter_create(NULL, pool, &conv_param,
341 &codec_port_data.conv);
342 if (status != PJ_SUCCESS) {
343 rc = 270; goto on_return;
344 }
345 }
346
347 status = pjmedia_vid_port_create(pool, &param, &renderer);
348 if (status != PJ_SUCCESS) {
349 rc = 230; goto on_return;
350 }
351
352 status = pjmedia_vid_port_connect(renderer, &codec_port,
353 PJ_FALSE);
354 } else {
355 status = pjmedia_vid_port_create(pool, &param, &renderer);
356 if (status != PJ_SUCCESS) {
357 rc = 230; goto on_return;
358 }
359
360 /* Connect avi port to renderer */
361 status = pjmedia_vid_port_connect(renderer, vid_port,
362 PJ_FALSE);
363 }
364
365 if (status != PJ_SUCCESS) {
366 rc = 240; goto on_return;
367 }
368 }
369
370 aud_stream = pjmedia_avi_streams_get_stream_by_media(avi_streams,
371 0,
372 PJMEDIA_TYPE_AUDIO);
373 aud_port = pjmedia_avi_stream_get_port(aud_stream);
374
375 if (aud_port) {
376 /* Create sound player port. */
377 status = pjmedia_snd_port_create_player(
378 pool, /* pool */
379 -1, /* use default dev. */
380 PJMEDIA_PIA_SRATE(&aud_port->info),/* clock rate. */
381 PJMEDIA_PIA_CCNT(&aud_port->info), /* # of channels. */
382 PJMEDIA_PIA_SPF(&aud_port->info), /* samples per frame. */
383 PJMEDIA_PIA_BITS(&aud_port->info), /* bits per sample. */
384 0, /* options */
385 &snd_port /* returned port */
386 );
387 if (status != PJ_SUCCESS) {
388 rc = 310; goto on_return;
389 }
390
391 /* Connect file port to the sound player.
392 * Stream playing will commence immediately.
393 */
394 status = pjmedia_snd_port_connect(snd_port, aud_port);
395 if (status != PJ_SUCCESS) {
396 rc = 330; goto on_return;
397 }
398 }
399
400 if (vid_port) {
401 pjmedia_vid_dev_cb cb;
402
403 pj_bzero(&cb, sizeof(cb));
404 avi_port.snd_port = snd_port;
405 avi_port.vid_port = renderer;
406 avi_port.is_running = PJ_TRUE;
407 pjmedia_vid_port_set_cb(renderer, &cb, &avi_port);
408
409 /* subscribe events */
410 pjmedia_event_subscribe(NULL, &avi_event_cb, &avi_port,
411 renderer);
412
413 if (snd_port) {
414 /* Synchronize video rendering and audio playback */
415 pjmedia_vid_port_set_clock_src(
416 renderer,
417 pjmedia_snd_port_get_clock_src(
418 snd_port, PJMEDIA_DIR_PLAYBACK));
419 }
420
421
422 /* Start video streaming.. */
423 status = pjmedia_vid_port_start(renderer);
424 if (status != PJ_SUCCESS) {
425 rc = 270; goto on_return;
426 }
427 }
428
429 while (!avi_port.is_quitting) {
430 pj_thread_sleep(100);
431 }
432
433on_return:
434 if (snd_port) {
435 pjmedia_snd_port_disconnect(snd_port);
436 /* Without this sleep, Windows/DirectSound will repeteadly
437 * play the last frame during destroy.
438 */
439 pj_thread_sleep(100);
440 pjmedia_snd_port_destroy(snd_port);
441 }
442 if (renderer) {
443 pjmedia_event_unsubscribe(NULL, &avi_event_cb, &avi_port,
444 renderer);
445 pjmedia_vid_port_destroy(renderer);
446 }
447 if (aud_port)
448 pjmedia_port_destroy(aud_port);
449 if (vid_port)
450 pjmedia_port_destroy(vid_port);
451 if (codec) {
452 pjmedia_vid_codec_close(codec);
453 pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec);
454 }
455
456 return rc;
457}
458
459
460static int main_func(int argc, char *argv[])
461{
462 pj_caching_pool cp;
463 pj_pool_t *pool;
464 int rc = 0;
465 pj_status_t status = PJ_SUCCESS;
466
467 if (argc != 2) {
468 puts("Error: filename required");
469 puts(desc);
470 return 1;
471 }
472
473
474 /* Must init PJLIB first: */
475 status = pj_init();
476 PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1);
477
478 /* Must create a pool factory before we can allocate any memory. */
479 pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
480
481 /* Create memory pool for our file player */
482 pool = pj_pool_create( &cp.factory, /* pool factory */
483 "AVI", /* pool name. */
484 4000, /* init size */
485 4000, /* increment size */
486 NULL /* callback on error */
487 );
488
489 pjmedia_video_format_mgr_create(pool, 64, 0, NULL);
490 pjmedia_converter_mgr_create(pool, NULL);
491 pjmedia_event_mgr_create(pool, 0, NULL);
492 pjmedia_vid_codec_mgr_create(pool, NULL);
493
494 status = pjmedia_vid_dev_subsys_init(&cp.factory);
495 if (status != PJ_SUCCESS)
496 goto on_return;
497
498 status = pjmedia_aud_subsys_init(&cp.factory);
499 if (status != PJ_SUCCESS) {
500 goto on_return;
501 }
502
503#if PJMEDIA_HAS_FFMPEG_VID_CODEC
504 status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory);
505 if (status != PJ_SUCCESS)
506 goto on_return;
507#endif
508
509 rc = aviplay(pool, argv[1]);
510
511 /*
512 * File should be playing and looping now
513 */
514
515 /* Without this sleep, Windows/DirectSound will repeteadly
516 * play the last frame during destroy.
517 */
518 pj_thread_sleep(100);
519
520on_return:
521#if PJMEDIA_HAS_FFMPEG_VID_CODEC
522 pjmedia_codec_ffmpeg_vid_deinit();
523#endif
524 pjmedia_aud_subsys_shutdown();
525 pjmedia_vid_dev_subsys_shutdown();
526
527 pjmedia_video_format_mgr_destroy(pjmedia_video_format_mgr_instance());
528 pjmedia_converter_mgr_destroy(pjmedia_converter_mgr_instance());
529 pjmedia_event_mgr_destroy(pjmedia_event_mgr_instance());
530 pjmedia_vid_codec_mgr_destroy(pjmedia_vid_codec_mgr_instance());
531
532 /* Release application pool */
533 pj_pool_release( pool );
534
535 /* Destroy pool factory */
536 pj_caching_pool_destroy( &cp );
537
538 /* Shutdown PJLIB */
539 pj_shutdown();
540
541 /* Done. */
542 return 0;
543}
544
545int main(int argc, char *argv[])
546{
547 return pj_run_app(&main_func, argc, argv, 0);
548}
549
550#else
551
552int main(int argc, char *argv[])
553{
554 PJ_UNUSED_ARG(argc);
555 PJ_UNUSED_ARG(argv);
556 puts("Error: this sample requires video capability (PJMEDIA_HAS_VIDEO == 1)");
557 return -1;
558}
559
560#endif /* PJMEDIA_HAS_VIDEO */