blob: b70396ecd00110821a1b7b0ca138d5dc7fe87cb8 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-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/* Video device with ffmpeg backend, currently only capture devices are
21 * implemented.
22 *
23 * Issues:
24 * - no device enumeration (ffmpeg limitation), so this uses "host API" enum
25 * instead
26 * - need stricter filter on "host API" enum, currently audio capture devs are
27 * still listed.
28 * - no format enumeration, currently hardcoded to PJMEDIA_FORMAT_RGB24 only
29 * - tested on Vista only (vfw backend) with virtual cam
30 * - vfw backend produce bottom up pictures
31 * - using VS IDE, this cannot run under debugger!
32 */
33
34#include <pjmedia-videodev/videodev_imp.h>
35#include <pj/assert.h>
36#include <pj/log.h>
37#include <pj/os.h>
38#include <pj/unicode.h>
39
40
41#if defined(PJMEDIA_VIDEO_DEV_HAS_FFMPEG) && PJMEDIA_VIDEO_DEV_HAS_FFMPEG != 0
42
43
44#define THIS_FILE "ffmpeg.c"
45
46#include "../pjmedia/ffmpeg_util.h"
47#include <libavdevice/avdevice.h>
48#include <libavformat/avformat.h>
49
50#define MAX_DEV_CNT 8
51
52typedef struct ffmpeg_dev_info
53{
54 pjmedia_vid_dev_info base;
55 AVInputFormat *host_api;
56 const char *def_devname;
57} ffmpeg_dev_info;
58
59
60typedef struct ffmpeg_factory
61{
62 pjmedia_vid_dev_factory base;
63 pj_pool_factory *pf;
64 pj_pool_t *pool;
65 pj_pool_t *dev_pool;
66 unsigned dev_count;
67 ffmpeg_dev_info dev_info[MAX_DEV_CNT];
68} ffmpeg_factory;
69
70
71typedef struct ffmpeg_stream
72{
73 pjmedia_vid_dev_stream base;
74 ffmpeg_factory *factory;
75 pj_pool_t *pool;
76 pjmedia_vid_dev_param param;
77 AVFormatContext *ff_fmt_ctx;
78} ffmpeg_stream;
79
80
81/* Prototypes */
82static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f);
83static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f);
84static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f);
85static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f);
86static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
87 unsigned index,
88 pjmedia_vid_dev_info *info);
89static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
90 pjmedia_vid_dev_factory *f,
91 unsigned index,
92 pjmedia_vid_dev_param *param);
93static pj_status_t ffmpeg_factory_create_stream(
94 pjmedia_vid_dev_factory *f,
95 pjmedia_vid_dev_param *param,
96 const pjmedia_vid_dev_cb *cb,
97 void *user_data,
98 pjmedia_vid_dev_stream **p_vid_strm);
99
100static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *strm,
101 pjmedia_vid_dev_param *param);
102static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *strm,
103 pjmedia_vid_dev_cap cap,
104 void *value);
105static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *strm,
106 pjmedia_vid_dev_cap cap,
107 const void *value);
108static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *strm);
109static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
110 pjmedia_frame *frame);
111static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *strm);
112static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *strm);
113
114/* Operations */
115static pjmedia_vid_dev_factory_op factory_op =
116{
117 &ffmpeg_factory_init,
118 &ffmpeg_factory_destroy,
119 &ffmpeg_factory_get_dev_count,
120 &ffmpeg_factory_get_dev_info,
121 &ffmpeg_factory_default_param,
122 &ffmpeg_factory_create_stream,
123 &ffmpeg_factory_refresh
124};
125
126static pjmedia_vid_dev_stream_op stream_op =
127{
128 &ffmpeg_stream_get_param,
129 &ffmpeg_stream_get_cap,
130 &ffmpeg_stream_set_cap,
131 &ffmpeg_stream_start,
132 &ffmpeg_stream_get_frame,
133 NULL,
134 &ffmpeg_stream_stop,
135 &ffmpeg_stream_destroy
136};
137
138
139static void print_ffmpeg_err(int err)
140{
141 char errbuf[512];
142 if (av_strerror(err, errbuf, sizeof(errbuf)) >= 0)
143 PJ_LOG(1, (THIS_FILE, "ffmpeg err %d: %s", err, errbuf));
144
145}
146
147static void print_ffmpeg_log(void* ptr, int level, const char* fmt, va_list vl)
148{
149 PJ_UNUSED_ARG(ptr);
150 PJ_UNUSED_ARG(level);
151 vfprintf(stdout, fmt, vl);
152}
153
154
155static pj_status_t ffmpeg_capture_open(AVFormatContext **ctx,
156 AVInputFormat *ifmt,
157 const char *dev_name,
158 const pjmedia_vid_dev_param *param)
159{
160 AVFormatParameters fp;
161 pjmedia_video_format_detail *vfd;
162 int err;
163
164 PJ_ASSERT_RETURN(ctx && ifmt && dev_name && param, PJ_EINVAL);
165 PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
166 PJ_EINVAL);
167
168 vfd = pjmedia_format_get_video_format_detail(&param->fmt, PJ_TRUE);
169
170 /* Init ffmpeg format context */
171 *ctx = avformat_alloc_context();
172
173 /* Init ffmpeg format param */
174 pj_bzero(&fp, sizeof(fp));
175 fp.prealloced_context = 1;
176 fp.width = vfd->size.w;
177 fp.height = vfd->size.h;
178 fp.pix_fmt = PIX_FMT_BGR24;
179 fp.time_base.num = vfd->fps.denum;
180 fp.time_base.den = vfd->fps.num;
181
182 /* Open capture stream */
183 err = av_open_input_stream(ctx, NULL, dev_name, ifmt, &fp);
184 if (err < 0) {
185 *ctx = NULL; /* ffmpeg freed its states on failure, do we must too */
186 print_ffmpeg_err(err);
187 return PJ_EUNKNOWN;
188 }
189
190 return PJ_SUCCESS;
191}
192
193static void ffmpeg_capture_close(AVFormatContext *ctx)
194{
195 if (ctx)
196 av_close_input_stream(ctx);
197}
198
199
200/****************************************************************************
201 * Factory operations
202 */
203/*
204 * Init ffmpeg_ video driver.
205 */
206pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf)
207{
208 ffmpeg_factory *f;
209 pj_pool_t *pool;
210
211 pool = pj_pool_create(pf, "ffmpeg_cap_dev", 1000, 1000, NULL);
212 f = PJ_POOL_ZALLOC_T(pool, ffmpeg_factory);
213
214 f->pool = pool;
215 f->pf = pf;
216 f->base.op = &factory_op;
217
218 avdevice_register_all();
219
220 return &f->base;
221}
222
223
224/* API: init factory */
225static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f)
226{
227 return ffmpeg_factory_refresh(f);
228}
229
230/* API: destroy factory */
231static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
232{
233 ffmpeg_factory *ff = (ffmpeg_factory*)f;
234 pj_pool_t *pool = ff->pool;
235
236 ff->dev_count = 0;
237 ff->pool = NULL;
238 if (ff->dev_pool)
239 pj_pool_release(ff->dev_pool);
240 if (pool)
241 pj_pool_release(pool);
242
243 return PJ_SUCCESS;
244}
245
246/* API: refresh the list of devices */
247static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f)
248{
249 ffmpeg_factory *ff = (ffmpeg_factory*)f;
250 AVInputFormat *p;
251 ffmpeg_dev_info *info;
252
253 av_log_set_callback(&print_ffmpeg_log);
254 av_log_set_level(AV_LOG_DEBUG);
255
256 if (ff->dev_pool) {
257 pj_pool_release(ff->dev_pool);
258 ff->dev_pool = NULL;
259 }
260
261 /* TODO: this should enumerate devices, now it enumerates host APIs */
262 ff->dev_count = 0;
263 ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL);
264
265 p = av_iformat_next(NULL);
266 while (p) {
267 if (p->flags & AVFMT_NOFILE) {
268 unsigned i;
269
270 info = &ff->dev_info[ff->dev_count++];
271 pj_bzero(info, sizeof(*info));
272 pj_ansi_strncpy(info->base.name, "default",
273 sizeof(info->base.name));
274 pj_ansi_snprintf(info->base.driver, sizeof(info->base.driver),
275 "%s (ffmpeg)", p->name);
276 info->base.dir = PJMEDIA_DIR_CAPTURE;
277 info->base.has_callback = PJ_FALSE;
278
279 info->host_api = p;
280
281#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
282 (defined(PJ_WIN64) && PJ_WIN64!=0)
283 info->def_devname = "0";
284#elif defined(PJ_LINUX) && PJ_LINUX!=0
285 info->def_devname = "/dev/video0";
286#endif
287
288 /* Set supported formats, currently hardcoded to RGB24 only */
289 info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
290 info->base.fmt_cnt = 1;
291 for (i = 0; i < info->base.fmt_cnt; ++i) {
292 pjmedia_format *fmt = &info->base.fmt[i];
293
294 fmt->id = PJMEDIA_FORMAT_RGB24;
295 fmt->type = PJMEDIA_TYPE_VIDEO;
296 fmt->detail_type = PJMEDIA_FORMAT_DETAIL_NONE;
297 }
298 }
299 p = av_iformat_next(p);
300 }
301
302 return PJ_SUCCESS;
303}
304
305/* API: get number of devices */
306static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f)
307{
308 ffmpeg_factory *ff = (ffmpeg_factory*)f;
309 return ff->dev_count;
310}
311
312/* API: get device info */
313static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
314 unsigned index,
315 pjmedia_vid_dev_info *info)
316{
317 ffmpeg_factory *ff = (ffmpeg_factory*)f;
318
319 PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
320
321 pj_memcpy(info, &ff->dev_info[index].base, sizeof(*info));
322
323 return PJ_SUCCESS;
324}
325
326/* API: create default device parameter */
327static pj_status_t ffmpeg_factory_default_param(pj_pool_t *pool,
328 pjmedia_vid_dev_factory *f,
329 unsigned index,
330 pjmedia_vid_dev_param *param)
331{
332 ffmpeg_factory *ff = (ffmpeg_factory*)f;
333 ffmpeg_dev_info *info;
334
335 PJ_ASSERT_RETURN(index < ff->dev_count, PJMEDIA_EVID_INVDEV);
336
337 PJ_UNUSED_ARG(pool);
338
339 info = &ff->dev_info[index];
340
341 pj_bzero(param, sizeof(*param));
342 param->dir = PJMEDIA_DIR_CAPTURE;
343 param->cap_id = index;
344 param->rend_id = PJMEDIA_VID_INVALID_DEV;
345 param->clock_rate = 0;
346
347 /* Set the device capabilities here */
348 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
349 param->clock_rate = 90000;
350 pjmedia_format_init_video(&param->fmt, 0, 320, 240, 25, 1);
351 param->fmt.id = info->base.fmt[0].id;
352
353 return PJ_SUCCESS;
354}
355
356
357
358/* API: create stream */
359static pj_status_t ffmpeg_factory_create_stream(
360 pjmedia_vid_dev_factory *f,
361 pjmedia_vid_dev_param *param,
362 const pjmedia_vid_dev_cb *cb,
363 void *user_data,
364 pjmedia_vid_dev_stream **p_vid_strm)
365{
366 ffmpeg_factory *ff = (ffmpeg_factory*)f;
367 pj_pool_t *pool;
368 ffmpeg_stream *strm;
369
370 PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
371 PJ_ASSERT_RETURN(param->dir == PJMEDIA_DIR_CAPTURE, PJ_EINVAL);
372 PJ_ASSERT_RETURN((unsigned)param->cap_id < ff->dev_count, PJ_EINVAL);
373 PJ_ASSERT_RETURN(param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO,
374 PJ_EINVAL);
375
376 PJ_UNUSED_ARG(cb);
377 PJ_UNUSED_ARG(user_data);
378
379 /* Create and Initialize stream descriptor */
380 pool = pj_pool_create(ff->pf, "ffmpeg-dev", 1000, 1000, NULL);
381 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
382
383 strm = PJ_POOL_ZALLOC_T(pool, struct ffmpeg_stream);
384 strm->factory = (ffmpeg_factory*)f;
385 strm->pool = pool;
386 pj_memcpy(&strm->param, param, sizeof(*param));
387
388 /* Done */
389 strm->base.op = &stream_op;
390 *p_vid_strm = &strm->base;
391
392 return PJ_SUCCESS;
393}
394
395/* API: Get stream info. */
396static pj_status_t ffmpeg_stream_get_param(pjmedia_vid_dev_stream *s,
397 pjmedia_vid_dev_param *pi)
398{
399 ffmpeg_stream *strm = (ffmpeg_stream*)s;
400
401 PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
402
403 pj_memcpy(pi, &strm->param, sizeof(*pi));
404
405 return PJ_SUCCESS;
406}
407
408/* API: get capability */
409static pj_status_t ffmpeg_stream_get_cap(pjmedia_vid_dev_stream *s,
410 pjmedia_vid_dev_cap cap,
411 void *pval)
412{
413 ffmpeg_stream *strm = (ffmpeg_stream*)s;
414
415 PJ_UNUSED_ARG(strm);
416 PJ_UNUSED_ARG(cap);
417 PJ_UNUSED_ARG(pval);
418
419 return PJMEDIA_EVID_INVCAP;
420}
421
422/* API: set capability */
423static pj_status_t ffmpeg_stream_set_cap(pjmedia_vid_dev_stream *s,
424 pjmedia_vid_dev_cap cap,
425 const void *pval)
426{
427 ffmpeg_stream *strm = (ffmpeg_stream*)s;
428
429 PJ_UNUSED_ARG(strm);
430 PJ_UNUSED_ARG(cap);
431 PJ_UNUSED_ARG(pval);
432
433 return PJMEDIA_EVID_INVCAP;
434}
435
436
437/* API: Start stream. */
438static pj_status_t ffmpeg_stream_start(pjmedia_vid_dev_stream *s)
439{
440 ffmpeg_stream *strm = (ffmpeg_stream*)s;
441 ffmpeg_dev_info *info;
442 pj_status_t status;
443
444 info = &strm->factory->dev_info[strm->param.cap_id];
445
446 PJ_LOG(4, (THIS_FILE, "Starting ffmpeg capture stream"));
447
448 status = ffmpeg_capture_open(&strm->ff_fmt_ctx, info->host_api,
449 info->def_devname, &strm->param);
450 if (status != PJ_SUCCESS) {
451 /* must set ffmpeg states to NULL on any failure */
452 strm->ff_fmt_ctx = NULL;
453 }
454
455 return status;
456}
457
458
459/* API: Get frame from stream */
460static pj_status_t ffmpeg_stream_get_frame(pjmedia_vid_dev_stream *s,
461 pjmedia_frame *frame)
462{
463 ffmpeg_stream *strm = (ffmpeg_stream*)s;
464 AVPacket p;
465 int err;
466
467 err = av_read_frame(strm->ff_fmt_ctx, &p);
468 if (err < 0) {
469 print_ffmpeg_err(err);
470 return PJ_EUNKNOWN;
471 }
472
473 pj_bzero(frame, sizeof(*frame));
474 frame->type = PJMEDIA_FRAME_TYPE_VIDEO;
475 frame->buf = p.data;
476 frame->size = p.size;
477
478 return PJ_SUCCESS;
479}
480
481
482/* API: Stop stream. */
483static pj_status_t ffmpeg_stream_stop(pjmedia_vid_dev_stream *s)
484{
485 ffmpeg_stream *strm = (ffmpeg_stream*)s;
486
487 PJ_LOG(4, (THIS_FILE, "Stopping ffmpeg capture stream"));
488
489 ffmpeg_capture_close(strm->ff_fmt_ctx);
490 strm->ff_fmt_ctx = NULL;
491
492 return PJ_SUCCESS;
493}
494
495
496/* API: Destroy stream. */
497static pj_status_t ffmpeg_stream_destroy(pjmedia_vid_dev_stream *s)
498{
499 ffmpeg_stream *strm = (ffmpeg_stream*)s;
500
501 PJ_ASSERT_RETURN(strm != NULL, PJ_EINVAL);
502
503 ffmpeg_stream_stop(s);
504
505 pj_pool_release(strm->pool);
506
507 return PJ_SUCCESS;
508}
509
510#ifdef _MSC_VER
511# pragma comment( lib, "avdevice.lib")
512# pragma comment( lib, "avformat.lib")
513# pragma comment( lib, "avutil.lib")
514#endif
515
516
517#endif /* PJMEDIA_VIDEO_DEV_HAS_FFMPEG */