From 0aa83d8efcf477675669569b037f291464c4f146 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Thu, 10 Apr 2014 10:01:07 +0000 Subject: Re #1758: Initial implementation of OpenH264 wrapper. Supports: - library detection via autoconf - CBP - packetization modes: 0, 1 - key frame request and indication - obey remote's fmtp Also added video codec test in samples (similar to the one in pjmedia test though). And there are some fixes here and there too (e.g. in vid_codec_util.c). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4815 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip-apps/build/Samples.mak | 1 + pjsip-apps/src/samples/aviplay.c | 9 + pjsip-apps/src/samples/simpleua.c | 8 + pjsip-apps/src/samples/vid_codec_test.c | 521 ++++++++++++++++++++++++++++++++ pjsip-apps/src/samples/vid_streamutil.c | 10 + 5 files changed, 549 insertions(+) create mode 100644 pjsip-apps/src/samples/vid_codec_test.c (limited to 'pjsip-apps') diff --git a/pjsip-apps/build/Samples.mak b/pjsip-apps/build/Samples.mak index 6ec7f191..d8769ff1 100644 --- a/pjsip-apps/build/Samples.mak +++ b/pjsip-apps/build/Samples.mak @@ -45,6 +45,7 @@ SAMPLES := auddemo \ streamutil \ strerror \ tonegen \ + vid_codec_test \ vid_streamutil PJSUA2_SAMPLES := pjsua2_demo diff --git a/pjsip-apps/src/samples/aviplay.c b/pjsip-apps/src/samples/aviplay.c index 35b05044..40dfaf90 100644 --- a/pjsip-apps/src/samples/aviplay.c +++ b/pjsip-apps/src/samples/aviplay.c @@ -500,6 +500,12 @@ static int main_func(int argc, char *argv[]) goto on_return; } +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, &cp.factory); + if (status != PJ_SUCCESS) + goto on_return; +#endif + #if PJMEDIA_HAS_FFMPEG_VID_CODEC status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); if (status != PJ_SUCCESS) @@ -520,6 +526,9 @@ static int main_func(int argc, char *argv[]) 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 pjmedia_aud_subsys_shutdown(); pjmedia_vid_dev_subsys_shutdown(); diff --git a/pjsip-apps/src/samples/simpleua.c b/pjsip-apps/src/samples/simpleua.c index 68c50b9b..b7f10250 100644 --- a/pjsip-apps/src/samples/simpleua.c +++ b/pjsip-apps/src/samples/simpleua.c @@ -385,6 +385,11 @@ int main(int argc, char *argv[]) status = pjmedia_vid_dev_subsys_init(&cp.factory); PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); +# if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, &cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); +# endif + # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 /* Init ffmpeg video codecs */ status = pjmedia_codec_ffmpeg_vid_init(NULL, &cp.factory); @@ -576,6 +581,9 @@ int main(int argc, char *argv[]) # if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC!=0 pjmedia_codec_ffmpeg_vid_deinit(); # endif +# if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +# endif #endif diff --git a/pjsip-apps/src/samples/vid_codec_test.c b/pjsip-apps/src/samples/vid_codec_test.c new file mode 100644 index 00000000..a249c71f --- /dev/null +++ b/pjsip-apps/src/samples/vid_codec_test.c @@ -0,0 +1,521 @@ +/* $Id$ */ +/* + * Copyright (C) 2014 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 + */ + + +/** + * \page page_pjmedia_samples_vid_codec_test_c Samples: Video Codec Test + * + * Video codec encode and decode test. + * + * This file is pjsip-apps/src/samples/vid_vodec_test.c + * + * \includelineno vid_vodec_test.c + */ + +#include +#include +#include +#include + + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + + +#include /* atoi() */ +#include + +#include "util.h" + + +static const char *desc = + " vid_vodec_test \n" +; + +#define THIS_FILE "vid_vodec_test.c" + + +/* If set, local renderer will be created to play original file */ +#define HAS_LOCAL_RENDERER_FOR_PLAY_FILE 1 + + +/* Default width and height for the renderer, better be set to maximum + * acceptable size. + */ +#define DEF_RENDERER_WIDTH 640 +#define DEF_RENDERER_HEIGHT 480 + + +/* Prototype */ +static void print_stream_stat(pjmedia_vid_stream *stream, + const pjmedia_vid_codec_param *codec_param); + +/* Prototype for LIBSRTP utility in file datatypes.c */ +int hex_string_to_octet_string(char *raw, char *hex, int len); + +/* + * Register all codecs. + */ +static pj_status_t init_codecs(pj_pool_factory *pf) +{ + pj_status_t status; + + /* To suppress warning about unused var when all codecs are disabled */ + PJ_UNUSED_ARG(status); + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + +#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 + status = pjmedia_codec_ffmpeg_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + + return PJ_SUCCESS; +} + +/* + * Register all codecs. + */ +static void deinit_codecs() +{ +#if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 + pjmedia_codec_ffmpeg_vid_deinit(); +#endif + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +#endif + +} + +/* + * usage() + */ +static void usage() +{ + puts(desc); +} + +static void show_diff(const pj_uint8_t *buf1, const pj_uint8_t *buf2, + unsigned size) +{ + enum { + STEP = 50 + }; + unsigned i=0; + + for (; i 0) + { + break; + } + } + nal_len[0] = i; + for (i = 0; i < size[1]; i++) { + if (memcmp(buf[1] + pos[1] + i, start_nal, + sizeof(start_nal)) == 0 && i > 0) + { + break; + } + } + nal_len[1] = i; + + if (nal_len[0] != nal_len[1]) { + printf("Different size in frame %d (%d vs %d)\n", + frame_cnt, nal_len[0], nal_len[1]); + } + + if (memcmp(buf[0]+pos[0], buf[1]+pos[1], nal_len[0]) != 0) { + printf("Mismatch in frame %d\n", frame_cnt); + show_diff(buf[0]+pos[0], buf[1]+pos[1], nal_len[0]); + puts(""); + ++mismatch_cnt; + } + + pos[0] += nal_len[0]; + pos[1] += nal_len[1]; + + if (pos[0] >= size[0]) + break; + } + + free(buf[0]); + free(buf[1]); + + if (!mismatch_cnt) + puts("Files the same!"); + else + printf("%d mismatches\n", mismatch_cnt); +} + +/* + * main() + */ +int main(int argc, char *argv[]) +{ + pj_caching_pool cp; + pjmedia_endpt *med_endpt; + pj_pool_t *pool; + pj_status_t status; + + /* Codec */ + char *codec_id = (char*)"H264"; + const pjmedia_vid_codec_info *codec_info; + pjmedia_vid_codec_param codec_param; + pjmedia_vid_codec *codec = NULL; + + //const char *save_filename = + // "/home/bennylp/Desktop/opt/src/openh264-svn/testbin/test.264"; + const char *save_filename = NULL; + + /* File */ + enum + { + WIDTH = 320, + HEIGHT = 192, + FPS = 12, + YUV_SIZE = WIDTH * HEIGHT * 3 >> 1, + YUV_BUF_SIZE = YUV_SIZE + WIDTH, + MAX_FRAMES = 32, + MTU = 1500 + }; + FILE *fyuv = NULL; + FILE *f264 = NULL; + typedef pj_uint8_t enc_buf_type[MTU]; + pj_uint8_t yuv_frame[YUV_BUF_SIZE]; + enc_buf_type enc_buf[MAX_FRAMES]; + unsigned read_cnt = 0, + pkt_cnt = 0, + dec_cnt = 0, + enc_cnt; + + if (0) { + diff_file(); + return 1; + } + + /* init PJLIB : */ + status = pj_init(); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Must create a pool factory before we can allocate any memory. */ + pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); + + /* Initialize media endpoint. */ + status = pjmedia_endpt_create(&cp.factory, NULL, 1, &med_endpt); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Create memory pool for application purpose */ + pool = pj_pool_create( &cp.factory, /* pool factory */ + "app", /* pool name. */ + 4000, /* init size */ + 4000, /* increment size */ + NULL /* callback on error */ + ); + + /* Init video format manager */ + pjmedia_video_format_mgr_create(pool, 64, 0, NULL); + + /* Init video converter manager */ + pjmedia_converter_mgr_create(pool, NULL); + + /* Init event manager */ + pjmedia_event_mgr_create(pool, 0, NULL); + + /* Init video codec manager */ + pjmedia_vid_codec_mgr_create(pool, NULL); + + /* Register all supported codecs */ + status = init_codecs(&cp.factory); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); + + /* Open YUV file */ + fyuv = fopen("pjsip-apps/bin/CiscoVT2people_320x192_12fps.yuv", "rb"); + if (!fyuv) { + puts("Unable to open ../CiscoVT2people_320x192_12fps.yuv"); + status = -1; + goto on_exit; + } + + /* Write 264 file if wanted */ + if (save_filename) { + f264 = fopen(save_filename, "wb"); + } + + /* Find which codec to use. */ + if (codec_id) { + unsigned count = 1; + pj_str_t str_codec_id = pj_str(codec_id); + + status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, + &str_codec_id, &count, + &codec_info, NULL); + if (status != PJ_SUCCESS) { + printf("Error: unable to find codec %s\n", codec_id); + return 1; + } + } else { + static pjmedia_vid_codec_info info[1]; + unsigned count = PJ_ARRAY_SIZE(info); + + /* Default to first codec */ + pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, NULL); + codec_info = &info[0]; + } + + /* Get codec default param for info */ + status = pjmedia_vid_codec_mgr_get_default_param(NULL, codec_info, + &codec_param); + pj_assert(status == PJ_SUCCESS); + + /* Alloc encoder */ + status = pjmedia_vid_codec_mgr_alloc_codec(NULL, codec_info, &codec); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error allocating codec")); + goto on_exit; + } + + codec_param.dir = PJMEDIA_DIR_ENCODING_DECODING; + codec_param.packing = PJMEDIA_VID_PACKING_PACKETS; + codec_param.enc_mtu = MTU; + codec_param.enc_fmt.det.vid.size.w = WIDTH; + codec_param.enc_fmt.det.vid.size.h = HEIGHT; + codec_param.enc_fmt.det.vid.fps.num = FPS; + codec_param.enc_fmt.det.vid.avg_bps = WIDTH * HEIGHT * FPS; + + status = pjmedia_vid_codec_init(codec, pool); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error initializing codec")); + goto on_exit; + } + + status = pjmedia_vid_codec_open(codec, &codec_param); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Error opening codec")); + goto on_exit; + } + + while (fread(yuv_frame, 1, YUV_SIZE, fyuv) == YUV_SIZE) { + pjmedia_frame frm_yuv, frm_enc[MAX_FRAMES]; + pj_bool_t has_more = PJ_FALSE; + const pj_uint8_t start_nal[] = { 0, 0, 1 }; + unsigned i; + + ++ read_cnt; + + pj_bzero(&frm_enc, sizeof(frm_enc)); + pj_bzero(&frm_yuv, sizeof(frm_yuv)); + + frm_yuv.buf = yuv_frame; + frm_yuv.size = YUV_SIZE; + + enc_cnt = 0; + frm_enc[enc_cnt].buf = enc_buf[enc_cnt]; + frm_enc[enc_cnt].size = MTU; + + status = pjmedia_vid_codec_encode_begin(codec, NULL, &frm_yuv, + MTU, &frm_enc[enc_cnt], + &has_more); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec encode error")); + goto on_exit; + } + if (frm_enc[enc_cnt].size) { + if (f264) { + fwrite(start_nal, 1, sizeof(start_nal), f264); + fwrite(frm_enc[enc_cnt].buf, 1, frm_enc[enc_cnt].size, f264); + } + ++pkt_cnt; + ++enc_cnt; + } + + while (has_more) { + + if (enc_cnt >= MAX_FRAMES) { + status = -1; + puts("Error: too many encoded frames"); + goto on_exit; + } + + has_more = PJ_FALSE; + frm_enc[enc_cnt].buf = enc_buf[enc_cnt]; + frm_enc[enc_cnt].size = MTU; + + status = pjmedia_vid_codec_encode_more(codec, MTU, + &frm_enc[enc_cnt], + &has_more); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec encode error")); + goto on_exit; + } + + if (frm_enc[enc_cnt].size) { + if (f264) { + fwrite(start_nal, 1, sizeof(start_nal), f264); + fwrite(frm_enc[enc_cnt].buf, 1, frm_enc[enc_cnt].size, + f264); + } + ++pkt_cnt; + ++enc_cnt; + } + } + + if (enc_cnt) { + frm_yuv.buf = yuv_frame; + frm_yuv.size = YUV_BUF_SIZE; + status = pjmedia_vid_codec_decode(codec, enc_cnt, + frm_enc, + YUV_BUF_SIZE, + &frm_yuv); + if (status != PJ_SUCCESS) { + PJ_PERROR(3,(THIS_FILE, status, "Codec decode error")); + goto on_exit; + } + + if (frm_yuv.size != 0) { + ++dec_cnt; + } + } + } + + printf("Done.\n" + " Read YUV frames: %d\n" + " Encoded packets: %d\n" + " Decoded YUV frames: %d\n", + read_cnt, pkt_cnt, dec_cnt); + + /* Start deinitialization: */ +on_exit: + if (codec) { + pjmedia_vid_codec_close(codec); + pjmedia_vid_codec_mgr_dealloc_codec(NULL, codec); + } + + if (f264) + fclose(f264); + + if (fyuv) + fclose(fyuv); + + /* Deinit codecs */ + deinit_codecs(); + + /* Destroy event manager */ + pjmedia_event_mgr_destroy(NULL); + + /* Release application pool */ + pj_pool_release( pool ); + + /* Destroy media endpoint. */ + pjmedia_endpt_destroy( med_endpt ); + + /* Destroy pool factory */ + pj_caching_pool_destroy( &cp ); + + /* Shutdown PJLIB */ + pj_shutdown(); + + return (status == PJ_SUCCESS) ? 0 : 1; +} + + +#else + +int main(int argc, char *argv[]) +{ + PJ_UNUSED_ARG(argc); + PJ_UNUSED_ARG(argv); + puts("Error: this sample requires video capability " + "(PJMEDIA_HAS_VIDEO == 1)"); + return -1; +} + +#endif /* PJMEDIA_HAS_VIDEO */ diff --git a/pjsip-apps/src/samples/vid_streamutil.c b/pjsip-apps/src/samples/vid_streamutil.c index f9f0a0d7..44c4f899 100644 --- a/pjsip-apps/src/samples/vid_streamutil.c +++ b/pjsip-apps/src/samples/vid_streamutil.c @@ -121,6 +121,11 @@ static pj_status_t init_codecs(pj_pool_factory *pf) /* To suppress warning about unused var when all codecs are disabled */ PJ_UNUSED_ARG(status); +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + status = pjmedia_codec_openh264_vid_init(NULL, pf); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); +#endif + #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 status = pjmedia_codec_ffmpeg_vid_init(NULL, pf); PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); @@ -137,6 +142,11 @@ static void deinit_codecs() #if defined(PJMEDIA_HAS_FFMPEG_VID_CODEC) && PJMEDIA_HAS_FFMPEG_VID_CODEC != 0 pjmedia_codec_ffmpeg_vid_deinit(); #endif + +#if defined(PJMEDIA_HAS_OPENH264_CODEC) && PJMEDIA_HAS_OPENH264_CODEC != 0 + pjmedia_codec_openh264_vid_deinit(); +#endif + } static pj_status_t create_file_player( pj_pool_t *pool, -- cgit v1.2.3