/* $Id$ */ /* * Copyright (C) 2011 Teluu Inc. (http://www.teluu.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" #include #include #include #include #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) #define THIS_FILE "vid_codec.c" /* * Capture device setting: * -1 = colorbar, * -2 = any non-colorbar capture device (first found) * x = specified capture device id */ #define CAPTURE_DEV -1 typedef struct codec_port_data_t { pjmedia_vid_codec *codec; pjmedia_vid_port *rdr_port; pj_uint8_t *enc_buf; pj_size_t enc_buf_size; pj_uint8_t *pack_buf; pj_size_t pack_buf_size; } codec_port_data_t; static pj_status_t codec_on_event(pjmedia_event *event, void *user_data) { codec_port_data_t *port_data = (codec_port_data_t*)user_data; if (event->type == PJMEDIA_EVENT_FMT_CHANGED) { pjmedia_vid_codec *codec = port_data->codec; pjmedia_vid_codec_param codec_param; pj_status_t status; status = pjmedia_vid_codec_get_param(codec, &codec_param); if (status != PJ_SUCCESS) return status; status = pjmedia_vid_dev_stream_set_cap( pjmedia_vid_port_get_stream(port_data->rdr_port), PJMEDIA_VID_DEV_CAP_FORMAT, &codec_param.dec_fmt); if (status != PJ_SUCCESS) return status; } return PJ_SUCCESS; } static pj_status_t codec_put_frame(pjmedia_port *port, pjmedia_frame *frame) { enum { MAX_PACKETS = 50 }; codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata; pj_status_t status; pjmedia_vid_codec *codec = port_data->codec; unsigned enc_cnt = 0; pj_uint8_t *enc_buf; unsigned enc_size_left; pjmedia_frame enc_frames[MAX_PACKETS]; pj_bool_t has_more = PJ_FALSE; enc_buf = port_data->enc_buf; enc_size_left = (unsigned)port_data->enc_buf_size; /* * Encode */ enc_frames[enc_cnt].buf = enc_buf; enc_frames[enc_cnt].size = enc_size_left; status = pjmedia_vid_codec_encode_begin(codec, NULL, frame, enc_size_left, &enc_frames[enc_cnt], &has_more); if (status != PJ_SUCCESS) goto on_error; enc_buf += enc_frames[enc_cnt].size; enc_size_left -= (unsigned)enc_frames[enc_cnt].size; ++enc_cnt; while (has_more) { enc_frames[enc_cnt].buf = enc_buf; enc_frames[enc_cnt].size = enc_size_left; status = pjmedia_vid_codec_encode_more(codec, enc_size_left, &enc_frames[enc_cnt], &has_more); if (status != PJ_SUCCESS) break; enc_buf += enc_frames[enc_cnt].size; enc_size_left -= (unsigned)enc_frames[enc_cnt].size; ++enc_cnt; if (enc_cnt >= MAX_PACKETS) { assert(!"Too many packets!"); break; } } /* * Decode */ status = pjmedia_vid_codec_decode(codec, enc_cnt, enc_frames, (unsigned)frame->size, frame); if (status != PJ_SUCCESS) goto on_error; /* Display */ status = pjmedia_port_put_frame( pjmedia_vid_port_get_passive_port(port_data->rdr_port), frame); if (status != PJ_SUCCESS) goto on_error; return PJ_SUCCESS; on_error: pj_perror(3, THIS_FILE, status, "codec_put_frame() error"); return status; } static const char* dump_codec_info(const pjmedia_vid_codec_info *info) { static char str[80]; unsigned i; char *p = str; /* Raw format ids */ for (i=0; (idec_fmt_id_cnt) && (p-str+5dec_fmt_id[i], 4); p += 4; *p++ = ' '; } *p = '\0'; return str; } static int enum_codecs() { unsigned i, cnt; pjmedia_vid_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; pj_status_t status; PJ_LOG(3, (THIS_FILE, " codec enums")); cnt = PJ_ARRAY_SIZE(info); status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &cnt, info, NULL); if (status != PJ_SUCCESS) return 100; for (i = 0; i < cnt; ++i) { PJ_LOG(3, (THIS_FILE, " %-16.*s %c%c %s", info[i].encoding_name.slen, info[i].encoding_name.ptr, (info[i].dir & PJMEDIA_DIR_ENCODING? 'E' : ' '), (info[i].dir & PJMEDIA_DIR_DECODING? 'D' : ' '), dump_codec_info(&info[i]))); } return PJ_SUCCESS; } static int encode_decode_test(pj_pool_t *pool, const char *codec_id, pjmedia_vid_packing packing) { const pj_str_t port_name = {"codec", 5}; pjmedia_vid_codec *codec=NULL; pjmedia_port codec_port; codec_port_data_t codec_port_data; pjmedia_vid_codec_param codec_param; const pjmedia_vid_codec_info *codec_info; const char *packing_name; pjmedia_vid_dev_index cap_idx, rdr_idx; pjmedia_vid_port *capture=NULL, *renderer=NULL; pjmedia_vid_port_param vport_param; pjmedia_video_format_detail *vfd; char codec_name[5]; pj_status_t status; int rc = 0; switch (packing) { case PJMEDIA_VID_PACKING_PACKETS: packing_name = "framed"; break; case PJMEDIA_VID_PACKING_WHOLE: packing_name = "whole"; break; default: packing_name = "unknown"; break; } PJ_LOG(3, (THIS_FILE, " encode decode test: codec=%s, packing=%s", codec_id, packing_name)); /* Lookup codec */ { pj_str_t codec_id_st; unsigned info_cnt = 1; /* Lookup codec */ pj_cstr(&codec_id_st, codec_id); status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st, &info_cnt, &codec_info, NULL); if (status != PJ_SUCCESS) { rc = 205; goto on_return; } } #if CAPTURE_DEV == -1 /* Lookup colorbar source */ status = pjmedia_vid_dev_lookup("Colorbar", "Colorbar generator", &cap_idx); if (status != PJ_SUCCESS) { rc = 206; goto on_return; } #elif CAPTURE_DEV == -2 /* Lookup any first non-colorbar source */ { unsigned i, cnt; pjmedia_vid_dev_info info; cap_idx = -1; cnt = pjmedia_vid_dev_count(); for (i = 0; i < cnt; ++i) { status = pjmedia_vid_dev_get_info(i, &info); if (status != PJ_SUCCESS) { rc = 206; goto on_return; } if (info.dir & PJMEDIA_DIR_CAPTURE && pj_ansi_stricmp(info.driver, "Colorbar")) { cap_idx = i; break; } } if (cap_idx == -1) { status = PJ_ENOTFOUND; rc = 206; goto on_return; } } #else cap_idx = CAPTURE_DEV; #endif /* Lookup SDL renderer */ status = pjmedia_vid_dev_lookup("SDL", "SDL renderer", &rdr_idx); if (status != PJ_SUCCESS) { rc = 207; goto on_return; } /* Prepare codec */ { pj_str_t codec_id_st; unsigned info_cnt = 1; /* Lookup codec */ pj_cstr(&codec_id_st, codec_id); status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, &codec_id_st, &info_cnt, &codec_info, NULL); if (status != PJ_SUCCESS) { rc = 245; goto on_return; } status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, &codec_param); if (status != PJ_SUCCESS) { rc = 246; goto on_return; } codec_param.packing = packing; /* Don't apply SDP fmtp */ codec_param.ignore_fmtp = PJ_TRUE; /* Open codec */ status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &codec); if (status != PJ_SUCCESS) { rc = 250; goto on_return; } status = pjmedia_vid_codec_init(codec, pool); if (status != PJ_SUCCESS) { rc = 251; goto on_return; } status = pjmedia_vid_codec_open(codec, &codec_param); if (status != PJ_SUCCESS) { rc = 252; goto on_return; } /* After opened, codec will update the param, let's sync encoder & * decoder format detail. */ codec_param.dec_fmt.det = codec_param.enc_fmt.det; /* Subscribe to codec events */ pjmedia_event_subscribe(NULL, &codec_on_event, &codec_port_data, codec); } pjmedia_vid_port_param_default(&vport_param); /* Create capture, set it to active (master) */ status = pjmedia_vid_dev_default_param(pool, cap_idx, &vport_param.vidparam); if (status != PJ_SUCCESS) { rc = 220; goto on_return; } pjmedia_format_copy(&vport_param.vidparam.fmt, &codec_param.dec_fmt); vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE; vport_param.active = PJ_TRUE; if (vport_param.vidparam.fmt.detail_type != PJMEDIA_FORMAT_DETAIL_VIDEO) { rc = 221; goto on_return; } vfd = pjmedia_format_get_video_format_detail(&vport_param.vidparam.fmt, PJ_TRUE); if (vfd == NULL) { rc = 225; goto on_return; } status = pjmedia_vid_port_create(pool, &vport_param, &capture); if (status != PJ_SUCCESS) { rc = 226; goto on_return; } /* Create renderer, set it to passive (slave) */ vport_param.active = PJ_FALSE; vport_param.vidparam.dir = PJMEDIA_DIR_RENDER; vport_param.vidparam.rend_id = rdr_idx; vport_param.vidparam.disp_size = vfd->size; status = pjmedia_vid_port_create(pool, &vport_param, &renderer); if (status != PJ_SUCCESS) { rc = 230; goto on_return; } /* Init codec port */ pj_bzero(&codec_port, sizeof(codec_port)); status = pjmedia_port_info_init2(&codec_port.info, &port_name, 0x1234, PJMEDIA_DIR_ENCODING, &codec_param.dec_fmt); if (status != PJ_SUCCESS) { rc = 260; goto on_return; } codec_port_data.codec = codec; codec_port_data.rdr_port = renderer; codec_port_data.enc_buf_size = codec_param.dec_fmt.det.vid.size.w * codec_param.dec_fmt.det.vid.size.h * 4; codec_port_data.enc_buf = pj_pool_alloc(pool, codec_port_data.enc_buf_size); codec_port_data.pack_buf_size = codec_port_data.enc_buf_size; codec_port_data.pack_buf = pj_pool_alloc(pool, codec_port_data.pack_buf_size); codec_port.put_frame = &codec_put_frame; codec_port.port_data.pdata = &codec_port_data; /* Connect capture to codec port */ status = pjmedia_vid_port_connect(capture, &codec_port, PJ_FALSE); if (status != PJ_SUCCESS) { rc = 270; goto on_return; } PJ_LOG(3, (THIS_FILE, " starting codec test: %s<->%.*s %dx%d", pjmedia_fourcc_name(codec_param.dec_fmt.id, codec_name), codec_info->encoding_name.slen, codec_info->encoding_name.ptr, codec_param.dec_fmt.det.vid.size.w, codec_param.dec_fmt.det.vid.size.h )); /* Start streaming.. */ status = pjmedia_vid_port_start(renderer); if (status != PJ_SUCCESS) { rc = 275; goto on_return; } status = pjmedia_vid_port_start(capture); if (status != PJ_SUCCESS) { rc = 280; goto on_return; } /* Sleep while the video is being displayed... */ pj_thread_sleep(10000); on_return: if (status != PJ_SUCCESS) { PJ_PERROR(3, (THIS_FILE, status, " error")); } if (capture) pjmedia_vid_port_stop(capture); if (renderer) pjmedia_vid_port_stop(renderer); if (capture) pjmedia_vid_port_destroy(capture); if (renderer) pjmedia_vid_port_destroy(renderer); if (codec) { pjmedia_event_unsubscribe(NULL, &codec_on_event, &codec_port_data, codec); pjmedia_vid_codec_close(codec); pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec); } return rc; } int vid_codec_test(void) { pj_pool_t *pool; int rc = 0; pj_status_t status; int orig_log_level; orig_log_level = pj_log_get_level(); pj_log_set_level(3); PJ_LOG(3, (THIS_FILE, "Performing video codec tests..")); pool = pj_pool_create(mem, "Vid codec test", 256, 256, 0); status = pjmedia_vid_dev_subsys_init(mem); if (status != PJ_SUCCESS) return -10; #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_OPENH264_CODEC status = pjmedia_codec_openh264_vid_init(NULL, mem); if (status != PJ_SUCCESS) { return -22; } #endif #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC status = pjmedia_codec_vid_toolbox_init(NULL, mem); if (status != PJ_SUCCESS) { return -23; } #endif #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VPX_CODEC status = pjmedia_codec_vpx_vid_init(NULL, mem); if (status != PJ_SUCCESS) { return -22; } #endif #if PJMEDIA_HAS_FFMPEG_VID_CODEC status = pjmedia_codec_ffmpeg_vid_init(NULL, mem); if (status != PJ_SUCCESS) return -20; #endif rc = enum_codecs(); if (rc != 0) goto on_return; #if PJMEDIA_HAS_FFMPEG_VID_CODEC rc = encode_decode_test(pool, "h263-1998", PJMEDIA_VID_PACKING_WHOLE); if (rc != 0) goto on_return; rc = encode_decode_test(pool, "h263-1998", PJMEDIA_VID_PACKING_PACKETS); if (rc != 0) goto on_return; #endif #if PJMEDIA_HAS_FFMPEG_VID_CODEC || PJMEDIA_HAS_OPENH264_CODEC || \ PJMEDIA_HAS_VID_TOOLBOX_CODEC rc = encode_decode_test(pool, "h264", PJMEDIA_VID_PACKING_WHOLE); if (rc != 0) goto on_return; rc = encode_decode_test(pool, "h264", PJMEDIA_VID_PACKING_PACKETS); if (rc != 0) goto on_return; #endif #if PJMEDIA_HAS_VPX_CODEC && PJMEDIA_HAS_VPX_CODEC_VP8 rc = encode_decode_test(pool, "vp8", PJMEDIA_VID_PACKING_WHOLE); if (rc != 0) goto on_return; rc = encode_decode_test(pool, "vp8", PJMEDIA_VID_PACKING_PACKETS); if (rc != 0) goto on_return; #endif #if PJMEDIA_HAS_VPX_CODEC && PJMEDIA_HAS_VPX_CODEC_VP9 rc = encode_decode_test(pool, "vp9", PJMEDIA_VID_PACKING_WHOLE); if (rc != 0) goto on_return; rc = encode_decode_test(pool, "vp9", PJMEDIA_VID_PACKING_PACKETS); if (rc != 0) goto on_return; #endif on_return: #if PJMEDIA_HAS_FFMPEG_VID_CODEC pjmedia_codec_ffmpeg_vid_deinit(); #endif #if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 pjmedia_codec_openh264_vid_deinit(); #endif #if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_VID_TOOLBOX_CODEC pjmedia_codec_vid_toolbox_deinit(); #endif #if defined(PJMEDIA_HAS_VPX_CODEC) && PJMEDIA_HAS_VPX_CODEC != 0 pjmedia_codec_vpx_vid_deinit(); #endif pjmedia_vid_dev_subsys_shutdown(); pj_pool_release(pool); pj_log_set_level(orig_log_level); return rc; } #endif /* PJMEDIA_HAS_VIDEO */