diff options
Diffstat (limited to 'pjmedia/src/test/vid_codec_test.c')
-rw-r--r-- | pjmedia/src/test/vid_codec_test.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/pjmedia/src/test/vid_codec_test.c b/pjmedia/src/test/vid_codec_test.c new file mode 100644 index 00000000..36ca19ac --- /dev/null +++ b/pjmedia/src/test/vid_codec_test.c @@ -0,0 +1,467 @@ +#include "test.h" +#include <pjmedia-codec/ffmpeg_codecs.h> +#include <pjmedia-videodev/videodev.h> +#include <pjmedia/vid_codec.h> +#include <pjmedia/port.h> + +#define THIS_FILE "vid_codec.c" + +#define BYPASS_CODEC 0 +#define BYPASS_PACKETIZER 0 + +/* + * 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_subscription *esub, + pjmedia_event *event) +{ + codec_port_data_t *port_data = (codec_port_data_t*)esub->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; + + ++event->proc_cnt; + + 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) +{ + codec_port_data_t *port_data = (codec_port_data_t*)port->port_data.pdata; + pj_status_t status; + +#if !BYPASS_CODEC + { + pjmedia_vid_codec *codec = port_data->codec; + pjmedia_frame enc_frame; + + enc_frame.buf = port_data->enc_buf; + enc_frame.size = port_data->enc_buf_size; + + status = pjmedia_vid_codec_encode(codec, frame, enc_frame.size, + &enc_frame); + if (status != PJ_SUCCESS) goto on_error; + +#if !BYPASS_PACKETIZER + if (enc_frame.size) { + unsigned pos = 0; + pj_bool_t packetized = PJ_FALSE; + unsigned unpack_pos = 0; + + while (pos < enc_frame.size) { + pj_uint8_t *payload; + pj_size_t payload_len; + + status = pjmedia_vid_codec_packetize( + codec, + (pj_uint8_t*)enc_frame.buf, + enc_frame.size, &pos, + (const pj_uint8_t**)&payload, + &payload_len); + if (status == PJ_ENOTSUP) + break; + if (status != PJ_SUCCESS) + goto on_error; + + status = pjmedia_vid_codec_unpacketize( + codec, payload, payload_len, + port_data->pack_buf, + port_data->pack_buf_size, + &unpack_pos); + if (status != PJ_SUCCESS) + goto on_error; + + // what happen if the bitstream is broken? + //if (i++ != 1) unpack_pos -= 10; + + packetized = PJ_TRUE; + } + + if (packetized) { + enc_frame.buf = port_data->pack_buf; + enc_frame.size = unpack_pos; + } + } +#endif + + status = pjmedia_vid_codec_decode(codec, &enc_frame, + frame->size, frame); + if (status != PJ_SUCCESS) goto on_error; + } +#endif + + 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; (i<info->dec_fmt_id_cnt) && (p-str+5<sizeof(str)); ++i) { + pj_memcpy(p, &info->dec_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) +{ + 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; + + 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; + pjmedia_event_subscription esub; + pj_status_t status; + int rc = 0; + + PJ_LOG(3, (THIS_FILE, " encode decode test")); + + /* 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; + const pjmedia_vid_codec_info *codec_info; + + /* 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; + } + +#if !BYPASS_CODEC + + /* 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_subscription_init(&esub, &codec_on_event, + &codec_port_data); + pjmedia_event_subscribe(&codec->epub, &esub); +#endif /* !BYPASS_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; + } + +#if BYPASS_CODEC + PJ_LOG(3, (THIS_FILE, " starting loopback test: %c%c%c%c %dx%d", + ((codec_param.dec_fmt.id & 0x000000FF) >> 0), + ((codec_param.dec_fmt.id & 0x0000FF00) >> 8), + ((codec_param.dec_fmt.id & 0x00FF0000) >> 16), + ((codec_param.dec_fmt.id & 0xFF000000) >> 24), + codec_param.dec_fmt.det.vid.size.w, + codec_param.dec_fmt.det.vid.size.h + )); +#else + PJ_LOG(3, (THIS_FILE, " starting codec test: %c%c%c%c<->%.*s %dx%d", + ((codec_param.dec_fmt.id & 0x000000FF) >> 0), + ((codec_param.dec_fmt.id & 0x0000FF00) >> 8), + ((codec_param.dec_fmt.id & 0x00FF0000) >> 16), + ((codec_param.dec_fmt.id & 0xFF000000) >> 24), + 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 + )); +#endif + + /* 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); + pjmedia_vid_port_destroy(capture); + } + if (renderer) { + pjmedia_vid_port_stop(renderer); + pjmedia_vid_port_destroy(renderer); + } + if (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(6); + + 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; + + status = pjmedia_codec_ffmpeg_init(NULL, mem); + if (status != PJ_SUCCESS) + return -20; + + rc = enum_codecs(); + if (rc != 0) + goto on_return; + + rc = encode_decode_test(pool, "h263-1998"); + if (rc != 0) + goto on_return; + +on_return: + pjmedia_codec_ffmpeg_deinit(); + pjmedia_vid_dev_subsys_shutdown(); + pj_pool_release(pool); + pj_log_set_level(orig_log_level); + + return rc; +} + + |