| /* $Id$ */ |
| /* |
| * Copyright (C) 2008-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 <pjmedia-codec.h> |
| |
| #define THIS_FILE "codec_vectors.c" |
| #define TMP_OUT "output.tmp" |
| |
| /* |
| * Encode test. Read input from WAV file, encode to temporary output file, |
| * and compare the temporary output file to the reference file. |
| */ |
| static int codec_test_encode(pjmedia_codec_mgr *mgr, |
| char *codec_name, |
| unsigned bitrate, |
| const char *wav_file, |
| const char *ref_encoded_file) |
| { |
| pj_str_t codec_id = pj_str(codec_name); |
| pj_pool_t *pool = NULL; |
| unsigned count, samples_per_frame; |
| pj_size_t encoded_frame_len = 0, pos; |
| pjmedia_codec *codec = NULL; |
| const pjmedia_codec_info *ci[1]; |
| pjmedia_codec_param codec_param; |
| pjmedia_port *wav_port = NULL; |
| pjmedia_frame in_frame, out_frame; |
| FILE *output = NULL, *fref = NULL; |
| int rc = 0; |
| pj_status_t status; |
| |
| pool = pj_pool_create(mem, "codec-vectors", 512, 512, NULL); |
| if (!pool) { |
| rc = -20; |
| goto on_return; |
| } |
| |
| /* Find and open the codec */ |
| count = 1; |
| status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id, &count, ci, NULL); |
| if (status != PJ_SUCCESS) { |
| rc = -30; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_mgr_alloc_codec(mgr, ci[0], &codec); |
| if (status != PJ_SUCCESS) { |
| rc = -40; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_mgr_get_default_param(mgr, ci[0], &codec_param); |
| if (status != PJ_SUCCESS) { |
| rc = -50; |
| goto on_return; |
| } |
| |
| codec_param.info.avg_bps = bitrate; |
| codec_param.setting.vad = 0; |
| |
| status = pjmedia_codec_init(codec, pool); |
| if (status != PJ_SUCCESS) { |
| rc = -60; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_open(codec, &codec_param); |
| if (status != PJ_SUCCESS) { |
| rc = -70; |
| goto on_return; |
| } |
| |
| /* Open WAV file */ |
| status = pjmedia_wav_player_port_create(pool, wav_file, |
| codec_param.info.frm_ptime, |
| PJMEDIA_FILE_NO_LOOP, 0, |
| &wav_port); |
| if (status != PJ_SUCCESS) { |
| rc = -80; |
| goto on_return; |
| } |
| |
| /* Open output file */ |
| output = fopen(TMP_OUT, "wb"); |
| if (!output) { |
| rc = -90; |
| goto on_return; |
| } |
| |
| /* Allocate buffer for PCM and encoded frames */ |
| samples_per_frame = codec_param.info.clock_rate * codec_param.info.frm_ptime / 1000; |
| in_frame.buf = pj_pool_alloc(pool, samples_per_frame * 2); |
| out_frame.buf = (pj_uint8_t*) pj_pool_alloc(pool, samples_per_frame); |
| |
| /* Loop read WAV file and encode and write to output file */ |
| for (;;) { |
| in_frame.size = samples_per_frame * 2; |
| in_frame.type = PJMEDIA_FRAME_TYPE_AUDIO; |
| |
| status = pjmedia_port_get_frame(wav_port, &in_frame); |
| if (status != PJ_SUCCESS || in_frame.type != PJMEDIA_FRAME_TYPE_AUDIO) |
| break; |
| |
| out_frame.size = samples_per_frame; |
| status = pjmedia_codec_encode(codec, &in_frame, samples_per_frame, |
| &out_frame); |
| if (status != PJ_SUCCESS) { |
| rc = -95; |
| goto on_return; |
| } |
| |
| if (out_frame.size) { |
| pj_size_t cnt; |
| |
| cnt = fwrite(out_frame.buf, out_frame.size, 1, output); |
| |
| if (encoded_frame_len == 0) |
| encoded_frame_len = out_frame.size; |
| } |
| } |
| |
| fclose(output); |
| output = NULL; |
| |
| /* Compare encoded files */ |
| fref = fopen(ref_encoded_file, "rb"); |
| if (!fref) { |
| rc = -100; |
| goto on_return; |
| } |
| |
| output = fopen(TMP_OUT, "rb"); |
| if (!output) { |
| rc = -110; |
| goto on_return; |
| } |
| |
| pos = 0; |
| for (;;) { |
| pj_size_t count; |
| |
| count = fread(in_frame.buf, encoded_frame_len, 1, fref); |
| if (count != 1) |
| break; |
| |
| count = fread(out_frame.buf, encoded_frame_len, 1, output); |
| if (count != 1) |
| break; |
| |
| if (memcmp(in_frame.buf, out_frame.buf, encoded_frame_len)) { |
| unsigned i; |
| pj_uint8_t *in = (pj_uint8_t*)in_frame.buf; |
| pj_uint8_t *out = (pj_uint8_t*)out_frame.buf; |
| |
| for (i=0; i<encoded_frame_len; ++i) { |
| if (in[i] != out[i]) |
| break; |
| } |
| |
| PJ_LOG(1,(THIS_FILE," failed: mismatch at pos %d", pos+i)); |
| rc = -200; |
| break; |
| } |
| |
| pos += encoded_frame_len; |
| } |
| |
| on_return: |
| if (output) |
| fclose(output); |
| |
| if (fref) |
| fclose(fref); |
| |
| if (codec) { |
| pjmedia_codec_close(codec); |
| pjmedia_codec_mgr_dealloc_codec(mgr, codec); |
| } |
| |
| if (wav_port) |
| pjmedia_port_destroy(wav_port); |
| |
| if (pool) |
| pj_pool_release(pool); |
| |
| return rc; |
| } |
| |
| |
| /* |
| * Read file in ITU format (".itu" extension). |
| * |
| * Set swap_endian to TRUE if the ITU file is stored in little |
| * endian format (normally true). |
| */ |
| static int read_ITU_format(FILE *fp_bitstream, |
| short *out_words, |
| short *p_frame_error_flag, |
| int number_of_16bit_words_per_frame, |
| pj_bool_t swap_endian) |
| { |
| enum { MAX_BITS_PER_FRAME = 160*8 }; |
| short i,j; |
| short nsamp; |
| short packed_word; |
| short bit_count; |
| short bit; |
| short in_array[MAX_BITS_PER_FRAME+2]; |
| short one = 0x0081; |
| short zero = 0x007f; |
| short frame_start = 0x6b21; |
| |
| nsamp = (short)fread(in_array, 2, 2 + 16*number_of_16bit_words_per_frame, |
| fp_bitstream); |
| |
| j = 0; |
| bit = in_array[j++]; |
| if (bit != frame_start) { |
| *p_frame_error_flag = 1; |
| } else { |
| *p_frame_error_flag = 0; |
| |
| /* increment j to skip over the number of bits in frame */ |
| j++; |
| |
| for (i=0; i<number_of_16bit_words_per_frame; i++) { |
| packed_word = 0; |
| bit_count = 15; |
| while (bit_count >= 0) { |
| bit = in_array[j++]; |
| if (bit == zero) |
| bit = 0; |
| else if (bit == one) |
| bit = 1; |
| else |
| *p_frame_error_flag = 1; |
| |
| packed_word <<= 1; |
| packed_word = (short )(packed_word + bit); |
| bit_count--; |
| } |
| |
| if (swap_endian) |
| out_words[i] = pj_ntohs(packed_word); |
| else |
| out_words[i] = packed_word; |
| } |
| } |
| return (nsamp-1)/16; |
| } |
| |
| |
| /* |
| * Decode test |
| * |
| * Decode the specified encoded file in "in_encoded_file" into temporary |
| * PCM output file, and compare the temporary PCM output file with |
| * the PCM reference file. |
| * |
| * Some reference file requires manipulation to the PCM output |
| * before comparison, such manipulation can be done by supplying |
| * this function with the "manip" function. |
| */ |
| static int codec_test_decode(pjmedia_codec_mgr *mgr, |
| char *codec_name, |
| unsigned bitrate, |
| unsigned encoded_len, |
| const char *in_encoded_file, |
| const char *ref_pcm_file, |
| void (*manip)(short *pcm, unsigned count)) |
| { |
| pj_str_t codec_id = pj_str(codec_name); |
| pj_pool_t *pool = NULL; |
| unsigned count, samples_per_frame, pos; |
| pjmedia_codec *codec = NULL; |
| const pjmedia_codec_info *ci[1]; |
| pjmedia_codec_param codec_param; |
| pjmedia_frame out_frame; |
| void *pkt; |
| FILE *input = NULL, *output = NULL, *fref = NULL; |
| pj_bool_t is_itu_format = PJ_FALSE; |
| int rc = 0; |
| pj_status_t status; |
| |
| pool = pj_pool_create(mem, "codec-vectors", 512, 512, NULL); |
| if (!pool) { |
| rc = -20; |
| goto on_return; |
| } |
| |
| /* Find and open the codec */ |
| count = 1; |
| status = pjmedia_codec_mgr_find_codecs_by_id(mgr, &codec_id, &count, ci, NULL); |
| if (status != PJ_SUCCESS) { |
| rc = -30; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_mgr_alloc_codec(mgr, ci[0], &codec); |
| if (status != PJ_SUCCESS) { |
| rc = -40; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_mgr_get_default_param(mgr, ci[0], &codec_param); |
| if (status != PJ_SUCCESS) { |
| rc = -50; |
| goto on_return; |
| } |
| |
| codec_param.info.avg_bps = bitrate; |
| codec_param.setting.vad = 0; |
| |
| status = pjmedia_codec_init(codec, pool); |
| if (status != PJ_SUCCESS) { |
| rc = -60; |
| goto on_return; |
| } |
| |
| status = pjmedia_codec_open(codec, &codec_param); |
| if (status != PJ_SUCCESS) { |
| rc = -70; |
| goto on_return; |
| } |
| |
| /* Open input file */ |
| input = fopen(in_encoded_file, "rb"); |
| if (!input) { |
| rc = -80; |
| goto on_return; |
| } |
| |
| /* Is the file in ITU format? */ |
| is_itu_format = pj_ansi_stricmp(in_encoded_file+strlen(in_encoded_file)-4, |
| ".itu")==0; |
| |
| /* Open output file */ |
| output = fopen(TMP_OUT, "wb"); |
| if (!output) { |
| rc = -90; |
| goto on_return; |
| } |
| |
| /* Allocate buffer for PCM and encoded frames */ |
| samples_per_frame = codec_param.info.clock_rate * codec_param.info.frm_ptime / 1000; |
| pkt = pj_pool_alloc(pool, samples_per_frame * 2); |
| out_frame.buf = (pj_uint8_t*) pj_pool_alloc(pool, samples_per_frame * 2); |
| |
| /* Loop read WAV file and encode and write to output file */ |
| for (;;) { |
| pjmedia_frame in_frame[2]; |
| pj_timestamp ts; |
| unsigned count; |
| pj_bool_t has_frame; |
| |
| if (is_itu_format) { |
| int nsamp; |
| short frame_err = 0; |
| |
| nsamp = read_ITU_format(input, (short*)pkt, &frame_err, |
| encoded_len / 2, PJ_TRUE); |
| if (nsamp != (int)encoded_len / 2) |
| break; |
| |
| has_frame = !frame_err; |
| } else { |
| if (fread(pkt, encoded_len, 1, input) != 1) |
| break; |
| |
| has_frame = PJ_TRUE; |
| } |
| |
| if (has_frame) { |
| count = 2; |
| if (pjmedia_codec_parse(codec, pkt, encoded_len, &ts, |
| &count, in_frame) != PJ_SUCCESS) |
| { |
| rc = -100; |
| goto on_return; |
| } |
| |
| if (count != 1) { |
| rc = -110; |
| goto on_return; |
| } |
| |
| if (pjmedia_codec_decode(codec, &in_frame[0], samples_per_frame*2, |
| &out_frame) != PJ_SUCCESS) |
| { |
| rc = -120; |
| goto on_return; |
| } |
| } else { |
| if (pjmedia_codec_recover(codec, samples_per_frame*2, |
| &out_frame) != PJ_SUCCESS) |
| { |
| rc = -125; |
| goto on_return; |
| } |
| } |
| |
| if (manip) |
| manip((short*)out_frame.buf, samples_per_frame); |
| |
| if (fwrite(out_frame.buf, out_frame.size, 1, output) != 1) { |
| rc = -130; |
| goto on_return; |
| } |
| } |
| |
| fclose(input); |
| input = NULL; |
| |
| fclose(output); |
| output = NULL; |
| |
| /* Compare encoded files */ |
| fref = fopen(ref_pcm_file, "rb"); |
| if (!fref) { |
| rc = -140; |
| goto on_return; |
| } |
| |
| output = fopen(TMP_OUT, "rb"); |
| if (!output) { |
| rc = -110; |
| goto on_return; |
| } |
| |
| pos = 0; |
| for (;;) { |
| pj_size_t count; |
| |
| count = fread(pkt, samples_per_frame*2, 1, fref); |
| if (count != 1) |
| break; |
| |
| count = fread(out_frame.buf, samples_per_frame*2, 1, output); |
| if (count != 1) |
| break; |
| |
| if (memcmp(pkt, out_frame.buf, samples_per_frame*2)) { |
| unsigned i; |
| pj_int16_t *in = (pj_int16_t*)pkt; |
| pj_int16_t *out = (pj_int16_t*)out_frame.buf; |
| |
| for (i=0; i<samples_per_frame; ++i) { |
| if (in[i] != out[i]) |
| break; |
| } |
| |
| PJ_LOG(1,(THIS_FILE," failed: mismatch at samples %d", pos+i)); |
| rc = -200; |
| break; |
| } |
| |
| pos += samples_per_frame; |
| } |
| |
| on_return: |
| if (output) |
| fclose(output); |
| |
| if (fref) |
| fclose(fref); |
| |
| if (input) |
| fclose(input); |
| |
| if (codec) { |
| pjmedia_codec_close(codec); |
| pjmedia_codec_mgr_dealloc_codec(mgr, codec); |
| } |
| |
| if (pool) |
| pj_pool_release(pool); |
| |
| return rc; |
| } |
| |
| #if PJMEDIA_HAS_G7221_CODEC |
| /* For ITU testing, off the 2 lsbs. */ |
| static void g7221_pcm_manip(short *pcm, unsigned count) |
| { |
| unsigned i; |
| for (i=0; i<count; i++) |
| pcm[i] &= 0xfffc; |
| |
| } |
| #endif /* PJMEDIA_HAS_G7221_CODEC */ |
| |
| int codec_test_vectors(void) |
| { |
| pjmedia_endpt *endpt; |
| pjmedia_codec_mgr *mgr; |
| int rc, rc_final = 0; |
| struct enc_vectors { |
| char *codec_name; |
| unsigned bit_rate; |
| const char *wav_file; |
| const char *ref_encoded_file; |
| } enc_vectors[] = |
| { |
| #if PJMEDIA_HAS_G7221_CODEC |
| { "G7221/16000/1", 24000, |
| "../src/test/vectors/g722_1_enc_in.wav", |
| "../src/test/vectors/g722_1_enc_out_24000_be.pak" |
| }, |
| { "G7221/16000/1", 32000, |
| "../src/test/vectors/g722_1_enc_in.wav", |
| "../src/test/vectors/g722_1_enc_out_32000_be.pak" |
| }, |
| #endif |
| { NULL } |
| }; |
| struct dec_vectors { |
| char *codec_name; |
| unsigned bit_rate; |
| unsigned encoded_frame_len; |
| void (*manip)(short *pcm, unsigned count); |
| const char *enc_file; |
| const char *ref_pcm_file; |
| } dec_vectors[] = |
| { |
| #if PJMEDIA_HAS_G7221_CODEC |
| { "G7221/16000/1", 24000, 60, |
| &g7221_pcm_manip, |
| "../src/test/vectors/g722_1_enc_out_24000_be.pak", |
| "../src/test/vectors/g722_1_dec_out_24000.pcm" |
| }, |
| { "G7221/16000/1", 32000, 80, |
| &g7221_pcm_manip, |
| "../src/test/vectors/g722_1_enc_out_32000_be.pak", |
| "../src/test/vectors/g722_1_dec_out_32000.pcm" |
| }, |
| { "G7221/16000/1", 24000, 60, |
| &g7221_pcm_manip, |
| "../src/test/vectors/g722_1_dec_in_24000_fe.itu", |
| "../src/test/vectors/g722_1_dec_out_24000_fe.pcm" |
| }, |
| { "G7221/16000/1", 32000, 80, |
| &g7221_pcm_manip, |
| "../src/test/vectors/g722_1_dec_in_32000_fe.itu", |
| "../src/test/vectors/g722_1_dec_out_32000_fe.pcm" |
| }, |
| #endif |
| { NULL } |
| }; |
| unsigned i; |
| pj_status_t status; |
| |
| status = pjmedia_endpt_create(mem, NULL, 0, &endpt); |
| if (status != PJ_SUCCESS) |
| return -5; |
| |
| mgr = pjmedia_endpt_get_codec_mgr(endpt); |
| |
| #if PJMEDIA_HAS_G7221_CODEC |
| status = pjmedia_codec_g7221_init(endpt); |
| if (status != PJ_SUCCESS) { |
| pjmedia_endpt_destroy(endpt); |
| return -7; |
| } |
| |
| /* Set shift value to zero for the test vectors */ |
| pjmedia_codec_g7221_set_pcm_shift(0); |
| #endif |
| |
| PJ_LOG(3,(THIS_FILE," encode tests:")); |
| for (i=0; i<PJ_ARRAY_SIZE(enc_vectors); ++i) { |
| if (!enc_vectors[i].codec_name) |
| continue; |
| PJ_LOG(3,(THIS_FILE," %s @%d bps %s ==> %s", |
| enc_vectors[i].codec_name, |
| enc_vectors[i].bit_rate, |
| enc_vectors[i].wav_file, |
| enc_vectors[i].ref_encoded_file)); |
| rc = codec_test_encode(mgr, enc_vectors[i].codec_name, |
| enc_vectors[i].bit_rate, |
| enc_vectors[i].wav_file, |
| enc_vectors[i].ref_encoded_file); |
| if (rc != 0) |
| rc_final = rc; |
| } |
| |
| PJ_LOG(3,(THIS_FILE," decode tests:")); |
| for (i=0; i<PJ_ARRAY_SIZE(dec_vectors); ++i) { |
| if (!dec_vectors[i].codec_name) |
| continue; |
| PJ_LOG(3,(THIS_FILE," %s @%d bps %s ==> %s", |
| dec_vectors[i].codec_name, |
| dec_vectors[i].bit_rate, |
| dec_vectors[i].enc_file, |
| dec_vectors[i].ref_pcm_file)); |
| rc = codec_test_decode(mgr, dec_vectors[i].codec_name, |
| dec_vectors[i].bit_rate, |
| dec_vectors[i].encoded_frame_len, |
| dec_vectors[i].enc_file, |
| dec_vectors[i].ref_pcm_file, |
| dec_vectors[i].manip); |
| if (rc != 0) |
| rc_final = rc; |
| } |
| |
| if (pj_file_exists(TMP_OUT)) |
| pj_file_delete(TMP_OUT); |
| |
| pjmedia_endpt_destroy(endpt); |
| return rc_final; |
| } |
| |